<?php

namespace App\Modules\Finance\Controllers;

use Core\Controller;
use Core\Auth;
use App\Modules\Finance\Models\InvoiceModel;

class InvoiceController extends Controller
{
    private $invoiceModel;

    public function __construct()
    {
        parent::__construct();
        $this->requireAuth();
        $this->invoiceModel = new InvoiceModel();
    }

    public function index()
    {
        $orgId = $this->scopedOrgId();
        $filters = [
            'project_id' => $this->getGet('project_id') ?: null,
            'contractor_id' => $this->getGet('contractor_id') ?: null,
            'status' => $this->getGet('status') ?: null,
            'date_from' => $this->getGet('date_from') ?: null,
            'date_to' => $this->getGet('date_to') ?: null,
        ];
        $page = max(1, (int)$this->getGet('page', 1));
        $perPage = 10;

        $invoices = $this->invoiceModel->getAllForOrg($orgId, $filters, $page, $perPage);
        $stats = $this->invoiceModel->getStats($orgId);
        $projects = $orgId !== null
            ? $this->dbQuery("SELECT id, name FROM projects WHERE org_id = ? ORDER BY name", [$orgId])
            : $this->dbQuery("SELECT id, name FROM projects ORDER BY name");
        $contractors = $orgId !== null
            ? $this->dbQuery("SELECT id, company_name FROM contractors WHERE org_id = ? ORDER BY company_name", [$orgId])
            : $this->dbQuery("SELECT id, company_name FROM contractors ORDER BY company_name");

        $this->render('finance/invoices/index', [
            'pageTitle' => 'Invoices',
            'invoices' => $invoices,
            'stats' => $stats,
            'filters' => $filters,
            'projects' => $projects,
            'contractors' => $contractors,
            'isPlatformView' => $orgId === null,
            'page' => $page,
            'perPage' => $perPage
        ]);
    }

    public function create()
    {
        $orgId = Auth::orgId();
        if ($orgId === null) {
            $this->setFlash('error', 'Invoices belong to an organization. Log in as an organization or contractor user to create one.');
            $this->redirect(APP_URL . '/finance/invoices');
        }
        $projects = $this->dbQuery("SELECT id, name FROM projects WHERE org_id = ? ORDER BY name", [$orgId]);
        $contractors = $this->dbQuery("SELECT id, company_name FROM contractors WHERE org_id = ? ORDER BY company_name", [$orgId]);

        $this->render('finance/invoices/create', [
            'pageTitle' => 'Create Invoice',
            'project_id' => $this->getGet('project_id'),
            'contractor_id' => $this->getGet('contractor_id'),
            'projects' => $projects,
            'contractors' => $contractors,
        ]);
    }

    public function store()
    {
        if (!$this->validateCsrf()) {
            $this->setFlash('error', 'Invalid security token.');
            $this->redirect(APP_URL . '/finance/invoices/create');
        }

        $orgId = Auth::orgId();
        if ($orgId === null) {
            $this->setFlash('error', 'Invoices belong to an organization. Log in as an organization or contractor user to create one.');
            $this->redirect(APP_URL . '/finance/invoices');
        }
        $projectId = (int)$this->getPost('project_id', 0);
        if (!$projectId) {
            $this->setFlash('error', 'Project is required.');
            $this->redirect(APP_URL . '/finance/invoices/create');
        }

        $user = Auth::user();
        $contractorId = null;
        $supplierId = null;
        if (($user['entity_type'] ?? '') === 'contractor_user') {
            $contractorId = (int)($user['entity_id'] ?? 0) ?: null;
        } elseif (($user['entity_type'] ?? '') === 'supplier_user') {
            $supplierId = (int)($user['entity_id'] ?? 0) ?: null;
        } else {
            $contractorId = (int)$this->getPost('contractor_id', 0) ?: null;
        }

        $items = $_POST['items'] ?? [];
        $subtotal = 0;
        foreach ($items as $item) {
            $subtotal += (float)($item['qty'] ?? 0) * (float)($item['unit_price'] ?? 0);
        }
        $taxPercent = (float)$this->getPost('tax_percent', 0);
        $taxAmount = ($subtotal * $taxPercent) / 100;

        $data = [
            'org_id' => $orgId,
            'project_id' => $projectId,
            'contractor_id' => $contractorId,
            'supplier_id' => $supplierId,
            'invoice_number' => $this->invoiceModel->generateInvoiceNumber($orgId),
            'invoice_date' => $this->getPost('date', date('Y-m-d')),
            'due_date' => $this->getPost('due_date') ?: null,
            'subtotal' => $subtotal,
            'tax_percent' => $taxPercent,
            'tax_amount' => $taxAmount,
            'total_amount' => $subtotal + $taxAmount,
            'currency' => $this->getPost('currency', DEFAULT_CURRENCY),
            'notes' => sanitize($this->getPost('notes', '')),
            'po_reference' => sanitize($this->getPost('po_reference', '')),
            'status' => 'draft',
            'submitted_by' => Auth::id(),
        ];

        $id = $this->invoiceModel->saveInvoice($data, $items);

        if (!empty($_FILES['attachment']['name'])) {
            $upload = $this->uploadFile($_FILES['attachment'], 'invoices');
            if ($upload) {
                $this->invoiceModel->addAttachment($id, $upload, $_FILES['attachment']['type'], Auth::id());
            }
        }

        logAudit('invoice_created', 'finance', 'invoice', $id);
        $this->setFlash('success', 'Invoice saved as draft.');
        $this->redirect(APP_URL . '/finance/invoices/' . $id);
    }

    public function show($id)
    {
        $invoice = $this->invoiceModel->getWithDetails((int)$id);
        if (!$invoice) {
            $this->setFlash('error', 'Invoice not found.');
            $this->redirect(APP_URL . '/finance/invoices');
        }
        $comments = $this->invoiceModel->getComments((int)$id);
        $this->render('finance/invoices/show', [
            'pageTitle' => 'Invoice ' . $invoice['invoice_number'],
            'invoice' => $invoice,
            'comments' => $comments
        ]);
    }

    public function edit($id)
    {
        $invoice = $this->invoiceModel->getWithDetails((int)$id);
        if (!$invoice || $invoice['status'] !== 'draft') {
            $this->setFlash('error', 'Only draft invoices can be edited.');
            $this->redirect(APP_URL . '/finance/invoices/' . $id);
        }
        $orgId = Auth::orgId();
        $projects = $this->dbQuery("SELECT id, name FROM projects WHERE org_id = ? ORDER BY name", [$orgId]);
        $contractors = $this->dbQuery("SELECT id, company_name FROM contractors WHERE org_id = ? ORDER BY company_name", [$orgId]);

        $this->render('finance/invoices/create', [
            'pageTitle' => 'Edit Invoice',
            'invoice' => $invoice,
            'projects' => $projects,
            'contractors' => $contractors,
        ]);
    }

    public function update($id)
    {
        if (!$this->validateCsrf()) {
            $this->setFlash('error', 'Invalid security token.');
            $this->redirect(APP_URL . '/finance/invoices/' . $id . '/edit');
        }

        $invoice = $this->invoiceModel->getWithDetails((int)$id);
        if (!$invoice || $invoice['status'] !== 'draft') {
            $this->setFlash('error', 'Only draft invoices can be edited.');
            $this->redirect(APP_URL . '/finance/invoices/' . $id);
        }

        $items = $_POST['items'] ?? [];
        $subtotal = 0;
        foreach ($items as $item) {
            $subtotal += (float)($item['qty'] ?? 0) * (float)($item['unit_price'] ?? 0);
        }
        $taxPercent = (float)$this->getPost('tax_percent', 0);
        $taxAmount = ($subtotal * $taxPercent) / 100;

        $data = [
            'project_id' => (int)$this->getPost('project_id', $invoice['project_id']),
            'invoice_date' => $this->getPost('date', $invoice['invoice_date']),
            'due_date' => $this->getPost('due_date') ?: null,
            'subtotal' => $subtotal,
            'tax_percent' => $taxPercent,
            'tax_amount' => $taxAmount,
            'total_amount' => $subtotal + $taxAmount,
            'notes' => sanitize($this->getPost('notes', '')),
            'po_reference' => sanitize($this->getPost('po_reference', '')),
        ];

        $this->invoiceModel->updateInvoice((int)$id, $data, $items);
        $this->setFlash('success', 'Invoice updated.');
        $this->redirect(APP_URL . '/finance/invoices/' . $id);
    }

    public function submit($id)
    {
        if (!$this->validateCsrf()) {
            $this->setFlash('error', 'Invalid security token.');
            $this->redirect(APP_URL . '/finance/invoices/' . $id);
        }

        $invoice = $this->invoiceModel->getWithDetails((int)$id);
        if (!$invoice) {
            $this->redirect(APP_URL . '/finance/invoices');
        }

        $this->invoiceModel->updateStatus((int)$id, 'submitted', ['submitted_at' => date('Y-m-d H:i:s')]);
        $this->notifyProjectManagers((int)$id, $invoice, 'submitted');
        logAudit('invoice_submitted', 'finance', 'invoice', (int)$id);
        $this->setFlash('success', 'Invoice submitted for review.');
        $this->redirect(APP_URL . '/finance/invoices/' . $id);
    }

    public function review($id)
    {
        if (!$this->validateCsrf()) {
            $this->redirect(APP_URL . '/finance/invoices/' . $id);
        }
        $this->invoiceModel->updateStatus((int)$id, 'under_review', ['reviewed_by' => Auth::id(), 'reviewed_at' => date('Y-m-d H:i:s')]);
        logAudit('invoice_review', 'finance', 'invoice', (int)$id);
        $this->redirect(APP_URL . '/finance/invoices/' . $id);
    }

    public function approve($id)
    {
        if (!$this->validateCsrf()) {
            $this->redirect(APP_URL . '/finance/invoices/' . $id);
        }
        $invoice = $this->invoiceModel->getWithDetails((int)$id);
        $this->invoiceModel->updateStatus((int)$id, 'approved', ['approved_by' => Auth::id(), 'approved_at' => date('Y-m-d H:i:s')]);
        $this->notifySubmitter((int)$id, $invoice, 'Your invoice ' . $invoice['invoice_number'] . ' has been approved.');
        logAudit('invoice_approved', 'finance', 'invoice', (int)$id);
        $this->redirect(APP_URL . '/finance/invoices/' . $id);
    }

    public function reject($id)
    {
        if (!$this->validateCsrf()) {
            $this->redirect(APP_URL . '/finance/invoices/' . $id);
        }
        $reason = sanitize($_POST['reason'] ?? '');
        $invoice = $this->invoiceModel->getWithDetails((int)$id);
        $this->invoiceModel->updateStatus((int)$id, 'rejected', ['rejection_reason' => $reason]);
        $this->notifySubmitter((int)$id, $invoice, 'Your invoice ' . $invoice['invoice_number'] . ' was rejected: ' . $reason);
        logAudit('invoice_rejected', 'finance', 'invoice', (int)$id, null, ['reason' => $reason]);
        $this->redirect(APP_URL . '/finance/invoices/' . $id);
    }

    public function markPaid($id)
    {
        if (!$this->validateCsrf()) {
            $this->redirect(APP_URL . '/finance/invoices/' . $id);
        }
        $invoice = $this->invoiceModel->getWithDetails((int)$id);
        $this->invoiceModel->updateStatus((int)$id, 'paid', ['paid_by' => Auth::id(), 'paid_at' => date('Y-m-d H:i:s')]);
        $this->notifySubmitter((int)$id, $invoice, 'Your invoice ' . $invoice['invoice_number'] . ' has been marked as paid.');
        logAudit('invoice_paid', 'finance', 'invoice', (int)$id);
        $this->redirect(APP_URL . '/finance/invoices/' . $id);
    }

    public function comment($id)
    {
        if (!$this->validateCsrf()) {
            $this->redirect(APP_URL . '/finance/invoices/' . $id);
        }
        $comment = sanitize($_POST['comment'] ?? '');
        if ($comment !== '') {
            $this->invoiceModel->addComment((int)$id, $comment, Auth::id());
        }
        $this->redirect(APP_URL . '/finance/invoices/' . $id);
    }

    public function downloadPdf($id)
    {
        $invoice = $this->invoiceModel->getWithDetails((int)$id);
        if (!$invoice) {
            $this->redirect(APP_URL . '/finance/invoices');
        }

        $html = \Core\View::render('finance/invoices/pdf', ['invoice' => $invoice]);
        $mpdf = new \Mpdf\Mpdf();
        $mpdf->WriteHTML($html);
        $mpdf->Output('invoice_' . $invoice['invoice_number'] . '.pdf', \Mpdf\Output\Destination::DOWNLOAD);
        exit;
    }

    public function export()
    {
        $orgId = Auth::orgId();
        $invoices = $this->invoiceModel->getAllForOrg($orgId, [], 1, 10000);

        header('Content-Type: text/csv');
        header('Content-Disposition: attachment; filename="invoices.csv"');
        $fp = fopen('php://output', 'w');
        fputcsv($fp, ['Invoice Number', 'Project', 'Contractor', 'Status', 'Date', 'Amount']);
        foreach ($invoices['data'] as $inv) {
            fputcsv($fp, [$inv['invoice_number'], $inv['project_name'], $inv['contractor_name'], $inv['status'], $inv['date'], $inv['total']]);
        }
        fclose($fp);
        exit;
    }

    private function notifyProjectManagers(int $invoiceId, array $invoice, string $event): void
    {
        $managers = $this->dbQuery("SELECT u.* FROM project_managers pm INNER JOIN users u ON pm.user_id = u.id WHERE pm.project_id = ?", [$invoice['project_id']]);
        foreach ($managers as $manager) {
            createNotification(
                (int)$manager['id'],
                'invoice_submitted',
                'Invoice Submitted for Review',
                'Invoice ' . $invoice['invoice_number'] . ' for ' . $invoice['project_name'] . ' needs your review.',
                ['invoice_id' => $invoiceId]
            );
            $attachments = [];
            foreach ($invoice['attachments'] ?? [] as $att) {
                $path = UPLOAD_PATH . '/' . $att['file_path'];
                if (file_exists($path)) {
                    $attachments[] = $path;
                }
            }
            $htmlBody = "<p>Hello " . htmlspecialchars($manager['first_name']) . ",</p>" .
                "<p>Invoice <strong>" . htmlspecialchars($invoice['invoice_number']) . "</strong> for project <strong>" . htmlspecialchars($invoice['project_name']) . "</strong> has been submitted and needs your review.</p>" .
                "<p>Amount: " . formatCurrency((float)$invoice['total_amount'], $invoice['currency'] ?? DEFAULT_CURRENCY) . "</p>" .
                "<p><a href=\"" . APP_URL . "/finance/invoices/{$invoiceId}\">Review Invoice</a></p>";
            sendEmail($manager['email'], trim($manager['first_name'] . ' ' . $manager['last_name']), 'Invoice Submitted — ' . $invoice['invoice_number'], $htmlBody, $attachments);
        }
    }

    private function notifySubmitter(int $invoiceId, array $invoice, string $message): void
    {
        $submitter = $this->dbQueryOne("SELECT * FROM users WHERE id = ?", [$invoice['submitted_by']]);
        if (!$submitter) {
            return;
        }
        createNotification((int)$submitter['id'], 'invoice_status', 'Invoice Update', $message, ['invoice_id' => $invoiceId]);
        sendEmail($submitter['email'], trim($submitter['first_name'] . ' ' . $submitter['last_name']), 'Invoice Update — ' . $invoice['invoice_number'], '<p>' . htmlspecialchars($message) . '</p>');
    }
}
