<?php

namespace App\Http\Controllers\Company\Banking;

use App\Exports\IssuedVoidChequesExport;
use App\Http\Controllers\Controller;
use App\Models\AuditLog;
use App\Models\ChequeBook;
use App\Models\ChequeFormat;
use App\Models\Setting;
use App\Models\Voucher;
use App\Services\ChequePrintService;
use App\Services\IssuedChequeService;
use Barryvdh\DomPDF\Facade\Pdf;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;
use Maatwebsite\Excel\Facades\Excel;

class IssuedVoidChequesController extends Controller
{
    public function __construct(
        private readonly IssuedChequeService $issuedCheques,
        private readonly ChequePrintService $chequePrint,
    ) {
    }

    private function companyId(): int
    {
        return auth()->user()->company_id;
    }

    private function gate(string $action = 'view'): void
    {
        if (!auth()->user()->hasPermission('banking', $action)) {
            abort(403);
        }
    }

    private function requireChequeRegister(): void
    {
        if (Setting::get($this->companyId(), 'use_cheque_register', '0') !== '1') {
            abort(403, 'Enable Use Cheque Register in Accounting Settings to access this feature.');
        }
    }

    public function index(Request $request)
    {
        $this->gate('view');
        $this->requireChequeRegister();

        $companyId = $this->companyId();
        $filters = $this->issuedCheques->parseFilters($request);
        $rows = $this->issuedCheques->paginate($companyId, $filters);
        $filterOptions = $this->issuedCheques->filterOptions($companyId);
        $formats = ChequeFormat::query()->orderBy('name')->get(['id', 'name']);

        return view('banking.issued-void-cheques.index', array_merge([
            'rows' => $rows,
            'filters' => $filters,
            'filterOptions' => $filterOptions,
            'filterSummary' => $this->issuedCheques->filterSummary($filters),
            'selectedPartyLabel' => $this->issuedCheques->selectedPartyLabel($filters),
            'formats' => $formats,
            'exportQuery' => $request->query(),
        ], $this->listPresentation($filters)));
    }

    public function export(Request $request)
    {
        $this->gate('view');
        $this->requireChequeRegister();

        $companyId = $this->companyId();
        $filters = $this->issuedCheques->parseFilters($request);
        $rows = $this->issuedCheques->list($companyId, $filters);
        $companyName = auth()->user()->company?->name ?? config('app.name');
        $filename = 'issued-void-cheques-' . now()->format('Y-m-d-His') . '.xlsx';

        return Excel::download(
            new IssuedVoidChequesExport(
                $rows,
                $filters['columns'],
                $this->issuedCheques->columnOptions(),
                $companyName,
                $this->issuedCheques->filterSummary($filters),
            ),
            $filename,
        );
    }

    public function print(Request $request)
    {
        $this->gate('view');
        $this->requireChequeRegister();

        $companyId = $this->companyId();
        $filters = $this->issuedCheques->parseFilters($request);
        $rows = $this->issuedCheques->list($companyId, $filters);

        return view('banking.issued-void-cheques.print', array_merge([
            'rows' => $rows,
            'companyName' => auth()->user()->company?->name ?? config('app.name'),
            'filterSummary' => $this->issuedCheques->filterSummary($filters),
        ], $this->listPresentation($filters)));
    }

    public function email(Request $request)
    {
        $this->gate('view');
        $this->requireChequeRegister();

        $validated = $request->validate([
            'recipient' => 'required|email|max:150',
            'subject' => 'nullable|string|max:180',
            'message' => 'nullable|string|max:2000',
            'format' => 'required|in:pdf,xlsx',
            'paper_size' => 'nullable|in:a4,letter,legal',
            'layout' => 'nullable|in:portrait,landscape',
        ]);

        $companyId = $this->companyId();
        $companyName = auth()->user()->company?->name ?? config('app.name');
        $filters = $this->issuedCheques->parseFilters($request);
        $rows = $this->issuedCheques->list($companyId, $filters);
        $filterSummary = $this->issuedCheques->filterSummary($filters);
        $subject = trim((string) ($validated['subject'] ?? ''))
            ?: "Issued & Void Cheques — {$companyName}";
        $message = trim((string) ($validated['message'] ?? ''))
            ?: "Please find the attached Issued & Void Cheques report for {$companyName}.\n\n{$filterSummary}";
        $attachment = $this->buildAttachment(
            $rows,
            $filters,
            $companyName,
            $filterSummary,
            $validated['format'],
            $validated['format'] === 'pdf' ? ($validated['paper_size'] ?? 'a4') : null,
            $validated['format'] === 'pdf' ? ($validated['layout'] ?? 'landscape') : null,
        );

        try {
            $this->configureCompanyMail($companyId);

            Mail::raw($message, function ($mail) use ($validated, $subject, $attachment, $companyName, $companyId) {
                $fromEmail = Setting::get($companyId, 'smtp_from_email');
                $fromName = Setting::get($companyId, 'smtp_from_name', $companyName);

                $mail->to($validated['recipient'])
                    ->from($fromEmail, $fromName)
                    ->subject($subject)
                    ->attachData($attachment['content'], $attachment['filename'], ['mime' => $attachment['mime']]);
            });

            $formatLabel = $validated['format'] === 'pdf' ? 'PDF' : 'Excel';

            return back()->with('success', "Issued & Void Cheques report ({$formatLabel}) emailed to {$validated['recipient']}.");
        } catch (Exception $e) {
            return back()
                ->withInput()
                ->with('error', 'Failed to email report: ' . $e->getMessage());
        }
    }

    public function void(Voucher $voucher)
    {
        $this->gate('view');
        $this->requireChequeRegister();
        $this->sameCompany($voucher);

        if (!in_array($voucher->voucher_type, ['bank_payment', 'contra'], true)) {
            abort(404);
        }

        $extra = $voucher->extra_data ?? [];
        if (($extra['cheque_status'] ?? '') === 'void') {
            return back()->with('error', 'This cheque is already void.');
        }

        $extra['cheque_status'] = 'void';
        $extra['cheque_voided_at'] = now()->toIso8601String();
        $voucher->update(['extra_data' => $extra]);

        AuditLog::record('void', 'issued_cheques', (string) $voucher->id, null, [
            'cheque_no' => $extra['cheque_no'] ?? null,
        ], 'Cheque voided: ' . ($extra['cheque_no'] ?? $voucher->voucher_number));

        return back()->with('success', 'Cheque marked as void.');
    }

    public function printCheque(Request $request, Voucher $voucher)
    {
        $this->gate('view');
        $this->requireChequeRegister();
        $this->sameCompany($voucher);

        if (Setting::get($this->companyId(), 'use_cheque_printing', '0') !== '1') {
            return back()->with('error', 'Enable Use Cheque Printing in Accounting Settings to print cheques.');
        }

        $companyId = $this->companyId();
        $validated = $request->validate([
            'cheque_format_id' => 'required|integer|exists:cheque_formats,id',
        ]);

        $format = ChequeFormat::query()->findOrFail($validated['cheque_format_id']);
        if ($format->company_id !== $companyId) {
            abort(403);
        }

        $extra = $voucher->extra_data ?? [];
        if (empty($extra['cheque_no']) || ($extra['cheque_status'] ?? '') === 'void') {
            return back()->with('error', 'This cheque cannot be printed.');
        }

        $voucher->load(['lines.account:id,code,name,is_bank,is_cash']);
        $presented = $this->chequePrint->presentVoucher($voucher, $companyId, true);
        [$words1, $words2] = $this->chequePrint->splitAmountWords($presented['amount_words']);

        if (empty($extra['cheque_printed_at'])) {
            $this->chequePrint->markPrinted($voucher);
        }

        return view('banking.cheque-print.print', [
            'format' => $format,
            'cheques' => [array_merge($presented, [
                'amount_words_1' => $words1,
                'amount_words_2' => $words2,
                'voucher' => $voucher,
            ])],
            'closeUrl' => route('banking.issued-void-cheques.index'),
        ]);
    }

    private function sameCompany(Voucher $voucher): void
    {
        if ($voucher->company_id !== $this->companyId()) {
            abort(403);
        }
    }

    private function listPresentation(array $filters): array
    {
        return [
            'visibleColumns' => $filters['columns'],
            'columnLabels' => $this->issuedCheques->columnOptions(),
            'selectedColumnsLabel' => $this->issuedCheques->selectedColumnsLabel($filters),
        ];
    }

    private function buildAttachment(
        $rows,
        array $filters,
        string $companyName,
        string $filterSummary,
        string $format,
        ?string $paperSize,
        ?string $layout,
    ): array {
        $presentation = $this->listPresentation($filters);

        if ($format === 'xlsx') {
            $export = new IssuedVoidChequesExport(
                $rows,
                $presentation['visibleColumns'],
                $presentation['columnLabels'],
                $companyName,
                $filterSummary,
            );

            return [
                'content' => Excel::raw($export, \Maatwebsite\Excel\Excel::XLSX),
                'filename' => 'issued-void-cheques-' . now()->format('Y-m-d-His') . '.xlsx',
                'mime' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
            ];
        }

        $pdf = Pdf::loadView('banking.issued-void-cheques.print', array_merge([
            'rows' => $rows,
            'companyName' => $companyName,
            'filterSummary' => $filterSummary,
            'forPdf' => true,
        ], $presentation))->setPaper($paperSize ?? 'a4', $layout ?? 'landscape');

        return [
            'content' => $pdf->output(),
            'filename' => 'issued-void-cheques-' . now()->format('Y-m-d-His') . '.pdf',
            'mime' => 'application/pdf',
        ];
    }

    private function configureCompanyMail(int $companyId): void
    {
        $host = trim((string) Setting::get($companyId, 'smtp_host', ''));
        $fromEmail = trim((string) Setting::get($companyId, 'smtp_from_email', ''));
        $fromName = trim((string) Setting::get($companyId, 'smtp_from_name', ''));

        if ($host === '' || $fromEmail === '' || $fromName === '') {
            throw new \RuntimeException('SMTP is not configured. Please set up outgoing email in Company Settings.');
        }

        config([
            'mail.default' => 'smtp',
            'mail.mailers.smtp.host' => $host,
            'mail.mailers.smtp.port' => (int) Setting::get($companyId, 'smtp_port', 587),
            'mail.mailers.smtp.username' => Setting::get($companyId, 'smtp_username', ''),
            'mail.mailers.smtp.password' => Setting::get($companyId, 'smtp_password', ''),
            'mail.mailers.smtp.encryption' => Setting::get($companyId, 'smtp_encryption', 'tls') === 'none'
                ? null
                : Setting::get($companyId, 'smtp_encryption', 'tls'),
            'mail.mailers.smtp.timeout' => 15,
            'mail.from.address' => $fromEmail,
            'mail.from.name' => $fromName,
        ]);
    }
}
