<?php

namespace App\Http\Controllers\Web\Payroll;

use App\Http\Controllers\Controller;
use App\Models\IncomeTaxDeclaration;
use App\Models\IncomeTaxDeclarationItem;
use App\Services\Payroll\IncomeTaxDeclarationService;
use App\Support\FinancialYear;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;

class IncomeTaxController extends Controller
{
    public function __construct(
        protected IncomeTaxDeclarationService $taxService
    ) {}

    public function preview(Request $request): JsonResponse
    {
        $user = $request->user();
        $companyId = (int) $user->company_id;

        $validated = $request->validate([
            'financial_year' => 'nullable|string|max:10',
            'regime' => 'required|in:old,new',
            'amounts' => 'nullable|array',
            'amounts.*' => 'nullable|numeric|min:0',
        ]);

        $fy = $validated['financial_year'] ?? FinancialYear::forDate();
        $projection = $this->taxService->previewProjection(
            $companyId,
            (int) $user->id,
            $validated['regime'],
            $validated['amounts'] ?? [],
            $fy
        );

        return response()->json([
            'projection' => $this->formatProjection($projection),
        ]);
    }

    public function store(Request $request)
    {
        $user = $request->user();
        $companyId = (int) $user->company_id;

        $validated = $request->validate([
            'financial_year' => 'nullable|string|max:10',
            'regime' => 'required|in:old,new',
            'amounts' => 'nullable|array',
            'amounts.*' => 'nullable|numeric|min:0',
        ]);

        $fy = $validated['financial_year'] ?? FinancialYear::forDate();
        $declaration = $this->taxService->getOrCreateDeclaration($companyId, (int) $user->id, $fy);

        try {
            $declaration = $this->taxService->saveDeclaration(
                $declaration,
                $validated['regime'],
                $validated['amounts'] ?? []
            );
        } catch (\RuntimeException $e) {
            return $this->respond($request, false, $e->getMessage());
        }

        return $this->respond($request, true, 'Declaration saved as draft.', $declaration, $companyId);
    }

    public function submit(Request $request)
    {
        $user = $request->user();
        $companyId = (int) $user->company_id;
        $fy = $request->input('financial_year', FinancialYear::forDate());

        $declaration = IncomeTaxDeclaration::withoutGlobalScopes()
            ->where('company_id', $companyId)
            ->where('user_id', $user->id)
            ->where('financial_year', $fy)
            ->with('items')
            ->firstOrFail();

        try {
            $declaration = $this->taxService->submit($declaration);
        } catch (\RuntimeException $e) {
            return $this->respond($request, false, $e->getMessage(), $declaration, $companyId);
        }

        return $this->respond($request, true, 'Declaration submitted for HR verification.', $declaration, $companyId);
    }

    public function uploadProof(Request $request, IncomeTaxDeclarationItem $item)
    {
        $user = $request->user();
        $declaration = $item->declaration;

        if ((int) $declaration->user_id !== (int) $user->id) {
            abort(403);
        }

        if (! $declaration->canUploadProofs()) {
            return back()->with('error', 'Proof cannot be uploaded for this declaration.');
        }

        $request->validate([
            'proof' => 'required|file|mimes:pdf,jpg,jpeg,png|max:5120',
        ]);

        $this->taxService->uploadProof($item, $request->file('proof'));

        return back()->with('success', 'Proof uploaded successfully.');
    }

    public function downloadProof(IncomeTaxDeclarationItem $item)
    {
        $user = auth()->user();
        $declaration = $item->declaration;

        $isOwner = (int) $declaration->user_id === (int) $user->id;
        $isHr = (bool) ($user->isTenantManager() ?? false);

        if (! $isOwner && ! $isHr) {
            abort(403);
        }

        if (! $item->proof_path || ! Storage::disk('local')->exists($item->proof_path)) {
            abort(404);
        }

        return Storage::disk('local')->download($item->proof_path);
    }

    public function adminReview(IncomeTaxDeclaration $declaration)
    {
        $user = auth()->user();
        if (! ($user->isTenantManager() ?? false)) {
            abort(403);
        }

        if ((int) $declaration->company_id !== (int) $user->company_id) {
            abort(403);
        }

        $declaration->load(['items', 'user.department', 'user.designation', 'plan']);

        return view('payroll.income-tax.admin-review', [
            'declaration' => $declaration,
            'projection' => $this->taxService->projectTax($declaration),
            'canFinalVerify' => $declaration->allProofsApproved(),
        ]);
    }

    public function adminVerify(Request $request, IncomeTaxDeclaration $declaration)
    {
        $user = $request->user();
        if (! ($user->isTenantManager() ?? false)) {
            abort(403);
        }

        if ((int) $declaration->company_id !== (int) $user->company_id) {
            abort(403);
        }

        $validated = $request->validate([
            'action' => 'required|in:verify,reject,bulk_approve,bulk_reject',
            'hr_remarks' => 'nullable|string|max:2000',
            'items' => 'nullable|array',
            'items.*.verified_amount' => 'nullable|numeric|min:0',
            'items.*.proof_status' => 'nullable|in:pending,approved,rejected',
            'items.*.review_remarks' => 'nullable|string|max:500',
        ]);

        if (in_array($validated['action'], ['bulk_approve', 'bulk_reject'], true)) {
            $declaration->loadMissing('items');
            $status = $validated['action'] === 'bulk_approve'
                ? IncomeTaxDeclarationItem::PROOF_APPROVED
                : IncomeTaxDeclarationItem::PROOF_REJECTED;
            $this->taxService->bulkReviewProofs($declaration, $status);

            return redirect()
                ->route('payroll.income-tax.admin.review', $declaration)
                ->with('success', 'All deduction lines updated.');
        }

        if (! empty($validated['items'])) {
            foreach ($validated['items'] as $itemId => $itemData) {
                $item = IncomeTaxDeclarationItem::find($itemId);
                if ($item && (int) $item->income_tax_declaration_id === (int) $declaration->id) {
                    $this->taxService->reviewItem(
                        $item,
                        $itemData['proof_status'] ?? IncomeTaxDeclarationItem::PROOF_APPROVED,
                        isset($itemData['verified_amount']) ? (float) $itemData['verified_amount'] : null,
                        $itemData['review_remarks'] ?? null
                    );
                }
            }
        }

        $declaration = $declaration->fresh(['items']);

        if ($validated['action'] === 'verify') {
            try {
                $this->taxService->verifyDeclaration($declaration, (int) $user->id, $validated['hr_remarks'] ?? null);
            } catch (\RuntimeException $e) {
                return redirect()
                    ->route('payroll.income-tax.admin.review', $declaration)
                    ->with('error', $e->getMessage());
            }

            return redirect()
                ->route('payroll.index', ['tab' => 'declaration', 'fy' => $declaration->financial_year])
                ->with('success', 'Declaration verified.');
        }

        if (empty($validated['hr_remarks'])) {
            return back()->with('error', 'Remarks are required when rejecting a declaration.');
        }

        $this->taxService->rejectDeclaration($declaration, (int) $user->id, $validated['hr_remarks']);

        return redirect()
            ->route('payroll.income-tax.admin.review', $declaration)
            ->with('success', 'Declaration rejected. Employee can revise and resubmit.');
    }

    public function form16(Request $request, ?string $financialYear = null)
    {
        $user = $request->user();
        $companyId = (int) $user->company_id;
        $fy = $financialYear ?? $request->query('fy', FinancialYear::forDate());

        try {
            $data = $this->taxService->form16Data($companyId, (int) $user->id, $fy);
        } catch (\Illuminate\Database\Eloquent\ModelNotFoundException) {
            return back()->with('error', 'Verified declaration not found for FY ' . $fy . '.');
        }

        $data['company'] = $user->company;
        $data['user'] = $user;

        if ($request->query('download') === '1') {
            if (! class_exists(\Barryvdh\DomPDF\Facade\Pdf::class)) {
                return back()->with('error', 'PDF download requires barryvdh/laravel-dompdf.');
            }

            $pdf = \Barryvdh\DomPDF\Facade\Pdf::loadView('payroll.income-tax.form16-pdf', $data);
            $filename = 'form16-' . $user->id . '-' . str_replace('-', '', $fy) . '.pdf';

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

        return view('payroll.income-tax.form16', $data);
    }

    public function adminForm16(Request $request, IncomeTaxDeclaration $declaration)
    {
        $user = $request->user();
        if (! ($user->isTenantManager() ?? false)) {
            abort(403);
        }

        if ((int) $declaration->company_id !== (int) $user->company_id) {
            abort(403);
        }

        $data = $this->taxService->form16Data(
            (int) $declaration->company_id,
            (int) $declaration->user_id,
            $declaration->financial_year
        );
        $data['company'] = $user->company;
        $data['user'] = $declaration->user;

        if ($request->query('download') === '1') {
            if (! class_exists(\Barryvdh\DomPDF\Facade\Pdf::class)) {
                return back()->with('error', 'PDF download requires barryvdh/laravel-dompdf.');
            }

            $pdf = \Barryvdh\DomPDF\Facade\Pdf::loadView('payroll.income-tax.form16-pdf', $data);
            $filename = 'form16-' . $declaration->user_id . '-' . str_replace('-', '', $declaration->financial_year) . '.pdf';

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

        return view('payroll.income-tax.form16', $data);
    }

    protected function respond(Request $request, bool $success, string $message, ?IncomeTaxDeclaration $declaration = null, ?int $companyId = null)
    {
        if ($request->expectsJson() || $request->ajax()) {
            $payload = [
                'success' => $success,
                'message' => $message,
            ];

            if ($declaration && $companyId) {
                $declaration->loadMissing('items');
                $windowOpen = $this->taxService->declarationWindowOpen($companyId);
                $payload['declaration'] = [
                    'id' => $declaration->id,
                    'regime' => $declaration->regime,
                    'status' => $declaration->status,
                ];
                $payload['ui'] = $declaration->uiState($windowOpen);
                $payload['projection'] = $this->formatProjection(
                    $this->taxService->projectTax($declaration)
                );
                $payload['missing_proofs'] = $this->taxService->missingProofSections($declaration);
            }

            return response()->json($payload, $success ? 200 : 422);
        }

        return $success
            ? back()->with('success', $message)
            : back()->with('error', $message);
    }

    /**
     * @param  array{taxable_income?: float, annual_tax?: float, monthly_tds?: float, deductions?: float, gross_annual?: float}  $projection
     * @return array{taxable_income: float, annual_tax: float, monthly_tds: float, deductions: float, gross_annual: float}
     */
    protected function formatProjection(array $projection): array
    {
        return [
            'taxable_income' => round((float) ($projection['taxable_income'] ?? 0), 2),
            'annual_tax' => round((float) ($projection['annual_tax'] ?? 0), 2),
            'monthly_tds' => round((float) ($projection['monthly_tds'] ?? 0), 2),
            'deductions' => round((float) ($projection['deductions'] ?? 0), 2),
            'gross_annual' => round((float) ($projection['gross_annual'] ?? 0), 2),
        ];
    }
}
