<?php

namespace App\Http\Controllers;

use App\Models\Branch;
use App\Models\Employee;
use App\Services\HrReportService;
use Barryvdh\DomPDF\Facade\Pdf;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Str;
use Inertia\Inertia;

class HrReportController extends Controller
{
    public function __construct(private HrReportService $reportService) {}

    public function index(Request $request)
    {
        if (! $this->canAccessReports()) {
            return redirect()->back()->with('error', __('Permission Denied.'));
        }

        $categories = $this->accessibleCategories();

        return Inertia::render('hr/reports/index', [
            'categories' => $categories,
        ]);
    }

    public function show(Request $request, string $category)
    {
        [$definition, $filters, $report] = $this->reportContext($request, $category);
        $relatedLinks = $this->resolveRelatedLinks($definition);

        return Inertia::render('hr/reports/show', [
            'category' => $category,
            'definition' => $definition,
            'report' => $report,
            'relatedLinks' => $relatedLinks,
            'filters' => $filters,
            'filterOptions' => $this->filterOptions($filters),
        ]);
    }

    public function export(Request $request, string $category)
    {
        [$definition, $filters, $report] = $this->reportContext($request, $category);
        $filename = $this->exportFilename($definition['title'], 'csv');
        $filterRows = $this->exportFilterRows($filters);

        return response()->streamDownload(function () use ($definition, $report, $filterRows) {
            $handle = fopen('php://output', 'w');
            fwrite($handle, "\xEF\xBB\xBF");

            fputcsv($handle, [$definition['title'] . ' Report']);

            if ($filterRows !== []) {
                fputcsv($handle, []);
                fputcsv($handle, ['Applied Filters']);
                fputcsv($handle, ['Filter', 'Value']);
                foreach ($filterRows as $row) {
                    fputcsv($handle, $row);
                }
            }

            fputcsv($handle, []);
            fputcsv($handle, ['Summary']);
            fputcsv($handle, ['Metric', 'Value']);
            foreach ($report['summary'] ?? [] as $item) {
                fputcsv($handle, [$item['label'] ?? '', $this->formatExportValue($item)]);
            }

            foreach ($report['tables'] ?? [] as $table) {
                fputcsv($handle, []);
                fputcsv($handle, [$table['title'] ?? 'Table']);
                fputcsv($handle, $table['columns'] ?? []);

                foreach ($table['rows'] ?? [] as $row) {
                    fputcsv($handle, $row);
                }
            }

            fclose($handle);
        }, $filename, [
            'Content-Type' => 'text/csv',
        ]);
    }

    public function exportPdf(Request $request, string $category)
    {
        [$definition, $filters, $report] = $this->reportContext($request, $category);
        $filename = $this->exportFilename($definition['title'], 'pdf');

        $pdf = Pdf::loadView('exports.hr-report-pdf', [
            'category' => $category,
            'definition' => $definition,
            'filters' => $filters,
            'filterRows' => $this->exportFilterRows($filters),
            'report' => $report,
            'generatedAt' => now(),
        ])->setPaper('a4', 'landscape');

        return $pdf->download($filename);
    }

    public function exportExcel(Request $request, string $category)
    {
        [$definition, $filters, $report] = $this->reportContext($request, $category);
        $filename = $this->exportFilename($definition['title'], 'xls');
        $excelSheets = $this->excelWorksheets($definition, $report, $filters);

        return response()->view('exports.hr-report-excel', [
            'category' => $category,
            'definition' => $definition,
            'filters' => $filters,
            'filterRows' => $this->exportFilterRows($filters),
            'report' => $report,
            'excelSheets' => $excelSheets,
            'generatedAt' => now(),
        ], 200, [
            'Content-Type' => 'application/vnd.ms-excel; charset=UTF-8',
            'Content-Disposition' => 'attachment; filename="' . $filename . '"',
            'Cache-Control' => 'max-age=0, no-cache, must-revalidate, proxy-revalidate',
            'Pragma' => 'public',
        ]);
    }

    private function accessibleCategories(): array
    {
        $categories = [];

        foreach (config('hr_reports.categories', []) as $key => $meta) {
            if (! Auth::user()->can($meta['permission'])) {
                continue;
            }

            $categories[] = [
                'key' => $key,
                'title' => $meta['title'],
                'description' => $meta['description'],
                'typical_reports' => $meta['typical_reports'],
                'filters' => $meta['filters'] ?? [],
                'permission' => $meta['permission'],
                'href' => route('hr.reports.show', $key),
            ];
        }

        return $categories;
    }

    private function resolveRelatedLinks(array $definition): array
    {
        return collect($definition['related_routes'] ?? [])
            ->filter(function ($link) {
                return (empty($link['permission']) || Auth::user()->can($link['permission']))
                    && ! empty($link['route'])
                    && Route::has($link['route']);
            })
            ->map(fn ($link) => [
                'label' => $link['label'],
                'href' => route($link['route']),
            ])
            ->values()
            ->all();
    }

    private function reportContext(Request $request, string $category): array
    {
        $definition = config("hr_reports.categories.{$category}");
        if (! $definition) {
            abort(404);
        }

        if (! Auth::user()->can($definition['permission'])) {
            return redirect()->back()->with('error', __('Permission Denied.'))->throwResponse();
        }

        $filters = $this->normalizeFilters($request);
        $report = $this->reportService->generate($category, $filters);

        return [$definition, $filters, $report];
    }

    private function normalizeFilters(Request $request): array
    {
        $validated = $request->validate([
            'branch_id' => ['nullable', 'integer'],
            'employee_id' => ['nullable', 'integer'],
            'start_date' => ['nullable', 'date'],
            'end_date' => ['nullable', 'date', 'after_or_equal:start_date'],
        ]);

        $companyIds = getCompanyAndUsersId();
        $branchId = isset($validated['branch_id']) && $validated['branch_id'] !== ''
            ? (int) $validated['branch_id']
            : null;
        $employeeId = isset($validated['employee_id']) && $validated['employee_id'] !== ''
            ? (int) $validated['employee_id']
            : null;

        if ($branchId && ! Branch::whereIn('created_by', $companyIds)->whereKey($branchId)->exists()) {
            $branchId = null;
        }

        if ($employeeId) {
            $employeeQuery = Employee::whereIn('created_by', $companyIds)->where('user_id', $employeeId);
            if ($branchId) {
                $employeeQuery->where('branch_id', $branchId);
            }

            if (! $employeeQuery->exists()) {
                $employeeId = null;
            }
        }

        return [
            'branch_id' => $branchId,
            'employee_id' => $employeeId,
            'start_date' => $validated['start_date'] ?? null,
            'end_date' => $validated['end_date'] ?? null,
        ];
    }

    private function filterOptions(array $filters): array
    {
        $companyIds = getCompanyAndUsersId();

        $branches = Branch::whereIn('created_by', $companyIds)
            ->orderBy('name')
            ->get(['id', 'name'])
            ->map(fn (Branch $branch) => [
                'value' => $branch->id,
                'label' => $branch->name,
            ])
            ->values()
            ->all();

        $employeesQuery = Employee::whereIn('created_by', $companyIds)
            ->with(['user:id,name', 'branch:id,name']);

        if (! empty($filters['branch_id'])) {
            $employeesQuery->where('branch_id', $filters['branch_id']);
        }

        $employees = $employeesQuery
            ->get()
            ->map(fn (Employee $employee) => [
                'value' => $employee->user_id,
                'label' => $employee->user?->name ?? ('Employee #' . $employee->user_id),
                'branch' => $employee->branch?->name,
                'branch_id' => $employee->branch_id,
            ])
            ->sortBy('label')
            ->values()
            ->all();

        return [
            'branches' => $branches,
            'employees' => $employees,
        ];
    }

    private function exportFilterRows(array $filters): array
    {
        $rows = [];

        if (! empty($filters['branch_id'])) {
            $branch = Branch::find($filters['branch_id']);
            $rows[] = ['Branch', $branch?->name ?? $filters['branch_id']];
        }

        if (! empty($filters['employee_id'])) {
            $employee = Employee::with('user')->where('user_id', $filters['employee_id'])->first();
            $rows[] = ['Employee', $employee?->user?->name ?? $filters['employee_id']];
        }

        if (! empty($filters['start_date'])) {
            $rows[] = ['Start Date', $filters['start_date']];
        }

        if (! empty($filters['end_date'])) {
            $rows[] = ['End Date', $filters['end_date']];
        }

        return $rows;
    }

    private function exportFilename(string $title, string $extension): string
    {
        return Str::slug($title) . '-report-' . now()->format('Ymd-His') . '.' . $extension;
    }

    private function excelWorksheets(array $definition, array $report, array $filters): array
    {
        $usedNames = [];
        $worksheets = [[
            'name' => $this->worksheetName('Summary', $usedNames),
            'title' => $definition['title'] . ' Report',
            'columns' => ['Metric', 'Value'],
            'rows' => collect($report['summary'] ?? [])
                ->map(fn (array $item) => [$item['label'] ?? '', $this->formatExportValue($item)])
                ->values()
                ->all(),
            'filterRows' => $this->exportFilterRows($filters),
        ]];

        foreach ($report['tables'] ?? [] as $table) {
            $worksheets[] = [
                'name' => $this->worksheetName($table['title'] ?? 'Table', $usedNames),
                'title' => $table['title'] ?? 'Table',
                'columns' => $table['columns'] ?? [],
                'rows' => $table['rows'] ?? [],
            ];
        }

        return $worksheets;
    }

    private function worksheetName(string $name, array &$usedNames): string
    {
        $baseName = trim(preg_replace('/[\[\]\:\*\?\/\\\\]/', ' ', $name));
        $baseName = $baseName !== '' ? $baseName : 'Sheet';
        $baseName = Str::limit($baseName, 31, '');
        $candidate = $baseName;
        $suffix = 2;

        while (in_array($candidate, $usedNames, true)) {
            $label = '-' . $suffix;
            $candidate = Str::limit($baseName, 31 - strlen($label), '') . $label;
            $suffix++;
        }

        $usedNames[] = $candidate;

        return $candidate;
    }

    private function formatExportValue(array $item): string
    {
        $value = $item['value'] ?? '';

        return match ($item['format'] ?? null) {
            'percent' => $value . '%',
            default => (string) $value,
        };
    }

    private function canAccessReports(): bool
    {
        if (Auth::user()->can('manage-reports')) {
            return true;
        }

        foreach (config('hr_reports.categories', []) as $meta) {
            if (Auth::user()->can($meta['permission'])) {
                return true;
            }
        }

        return false;
    }
}
