<?php
declare(strict_types=1);

namespace App\Controllers;

use App\Core\Auth;
use App\Core\Database;
use App\Core\Request;
use App\Models\PortalData;
use App\Services\DocumentStore;

final class PortalController extends BaseController
{
    public function invoices(Request $request): void
    {
        $this->requirePage('invoices');
        view('invoices/index', ['title' => 'Invoices', 'rows' => PortalData::invoices()]);
    }

    public function invoiceFile(Request $request): void
    {
        $this->requirePage('invoices');
        $row = PortalData::invoice((int) $request->param('id'));
        if (!$row || empty($row['file_name'])) {
            http_response_code(404);
            echo 'Not found.';
            return;
        }
        $tid = (int) Auth::instance()->tenantId();
        if (!DocumentStore::existsEntity($tid, 'invoice', (int) $row['id'])) {
            http_response_code(404);
            echo 'No PDF on this invoice.';
            return;
        }
        DocumentStore::streamEntity($tid, 'invoice', (int) $row['id'], (string) $row['file_name']);
    }

    public function contractFile(Request $request): void
    {
        $this->requirePage('contract');
        $row = PortalData::contract((int) $request->param('id'));
        if (!$row || empty($row['file_name'])) {
            http_response_code(404);
            echo 'Not found.';
            return;
        }
        $tid = (int) Auth::instance()->tenantId();
        if (!DocumentStore::existsEntity($tid, 'contract', (int) $row['id'])) {
            http_response_code(404);
            echo 'No PDF on this contract.';
            return;
        }
        DocumentStore::streamEntity($tid, 'contract', (int) $row['id'], (string) $row['file_name']);
    }

    public function documents(Request $request): void
    {
        $this->requirePage('documents');
        $tab = (string) ($_GET['tab'] ?? 'quotes');
        if (!in_array($tab, PortalData::documentTabs(), true)) {
            $tab = 'quotes';
        }
        view('documents/index', [
            'title' => 'Documents',
            'tab' => $tab,
            'counts' => PortalData::documentTabCounts(),
            'rows' => PortalData::documents($tab),
        ]);
    }

    public function file(Request $request): void
    {
        $this->assertCanOpenFiles();
        $row = PortalData::document((int) $request->param('id'));
        if (!$row || empty($row['file_name'])) {
            http_response_code(404);
            echo 'Not found.';
            return;
        }
        $tid = (int) Auth::instance()->tenantId();
        if (!DocumentStore::exists($tid, (int) $row['id'])) {
            http_response_code(404);
            echo 'No PDF on this paper.';
            return;
        }
        DocumentStore::stream($tid, (int) $row['id'], (string) $row['file_name']);
    }

    public function signed(Request $request): void
    {
        $this->assertCanOpenFiles();
        $row = PortalData::document((int) $request->param('id'));
        if (!$row || ($row['sign_status'] ?? '') !== 'signed' || empty($row['signed_file_name'])) {
            http_response_code(404);
            echo 'No signed PDF yet.';
            return;
        }
        $tid = (int) Auth::instance()->tenantId();
        if (!DocumentStore::exists($tid, (int) $row['id'], true)) {
            http_response_code(404);
            echo 'No signed PDF yet.';
            return;
        }
        DocumentStore::stream($tid, (int) $row['id'], (string) $row['signed_file_name'], true);
    }

    public function sign(Request $request): void
    {
        $this->requirePage('sign');
        view('sign/index', ['title' => 'Sign a paper', 'rows' => PortalData::pendingSigns()]);
    }

    public function signSave(Request $request): void
    {
        $this->requirePage('sign');
        $this->verifyCsrf($request);
        $name = trim((string) $request->input('signer_name', ''));
        if ($name === '' || mb_strlen($name) < 2) {
            $this->failSign('Type your name to sign.');
        }
        $agree = (string) $request->input('agree');
        if ($agree !== '1') {
            $this->failSign('Confirm that you agree to sign.');
        }
        $file = $_FILES['signed_pdf'] ?? [];
        try {
            $fileName = DocumentStore::accept(is_array($file) ? $file : []);
        } catch (\RuntimeException $e) {
            $this->failSign($e->getMessage());
        }
        $pending = PortalData::pendingSign((int) $request->param('id'));
        if (!$pending) {
            $this->failSign('This paper is not waiting for you.', 404);
        }
        try {
            DocumentStore::save((int) $pending['document_id'], (string) $file['tmp_name'], true);
        } catch (\RuntimeException $e) {
            $this->failSign($e->getMessage(), 500);
        }
        $ok = PortalData::sign((int) $request->param('id'), $name, $fileName);
        if (!$ok) {
            $this->failSign('This paper is not waiting for you.', 404);
        }
        $this->notifyStaff('Document signed', $name . ' uploaded a signed PDF.', '/documents', ['salesperson', 'manager']);
        if (is_ajax()) {
            json_out(['ok' => true, 'message' => 'Signed. Thank you.']);
        }
        flash('success', 'Signed. Your company can open the signed PDF.');
        redirect('sign');
    }

    public function orders(Request $request): void
    {
        $this->requirePage('orders');
        view('orders/index', ['title' => 'My orders', 'rows' => PortalData::orders()]);
    }

    public function contract(Request $request): void
    {
        $this->requirePage('contract');
        view('contract/index', ['title' => 'My contract', 'rows' => PortalData::contracts()]);
    }

    public function equipment(Request $request): void
    {
        $this->requirePage('equipment');
        view('equipment/index', ['title' => 'My equipment', 'rows' => PortalData::equipment()]);
    }

    public function service(Request $request): void
    {
        $this->requirePage('service_request');
        view('service/index', ['title' => 'Service request', 'jobs' => PortalData::jobs()]);
    }

    public function serviceSave(Request $request): void
    {
        $this->requirePage('service_request');
        $this->verifyCsrf($request);
        $subject = trim((string) $request->input('subject', ''));
        $body = trim((string) $request->input('body', ''));
        $when = trim((string) $request->input('visit_at', ''));
        $ts = $when !== '' ? strtotime(str_replace('T', ' ', $when)) : false;
        if ($subject === '' || $body === '' || $ts === false) {
            if (is_ajax()) {
                json_out(['ok' => false, 'error' => 'Subject, visit date, and details are required.'], 422);
            }
            flash('error', 'Subject, visit date, and details are required.');
            redirect('service');
        }
        $visitAt = date('Y-m-d H:i:s', $ts);
        PortalData::createRequest($subject, $body, Auth::instance()->id(), $visitAt);
        $this->notifyStaff('Customer service request', $subject . ' · ' . date('M j, g:i A', $ts), '/jobs');
        if (is_ajax()) {
            json_out(['ok' => true, 'message' => 'Request sent. Your service team will see it.']);
        }
        flash('success', 'Request sent. Your service team will see it.');
        redirect('service');
    }

    /** @param list<string> $roles */
    private function notifyStaff(string $title, string $body, string $url, array $roles = ['service', 'manager']): void
    {
        $tid = Auth::instance()->tenantId();
        if (!$tid) {
            return;
        }
        $allowed = ['salesperson', 'manager', 'admin', 'service', 'marketing'];
        $roles = array_values(array_intersect($allowed, $roles));
        if ($roles === []) {
            return;
        }
        $in = implode(',', array_fill(0, count($roles), '?'));
        $people = Database::instance()->fetchAll(
            "SELECT u.id FROM users u
             INNER JOIN user_roles ur ON ur.user_id = u.id AND ur.tenant_id = ?
             INNER JOIN roles r ON r.id = ur.role_id AND r.code IN ($in)
             WHERE u.status = 'active' AND u.deleted_at IS NULL",
            array_merge([$tid], $roles)
        );
        foreach ($people as $p) {
            Database::instance()->query(
                'INSERT INTO notifications (tenant_id, user_id, title, body, url) VALUES (:tid, :uid, :t, :b, :u)',
                ['tid' => $tid, 'uid' => (int) $p['id'], 't' => mb_substr($title, 0, 180), 'b' => mb_substr($body, 0, 255), 'u' => $url]
            );
        }
    }

    private function assertCanOpenFiles(): void
    {
        $auth = Auth::instance();
        if (!$auth->canSee('documents') && !$auth->canSee('sign')) {
            http_response_code(403);
            view('errors/403', ['title' => 'No access']);
            exit;
        }
    }

    private function failSign(string $message, int $code = 422): never
    {
        if (is_ajax()) {
            json_out(['ok' => false, 'error' => $message], $code);
        }
        flash('error', $message);
        redirect('sign');
    }
}
