<?php
declare(strict_types=1);

namespace App\Controllers;

use App\Core\Auth;
use App\Core\Request;
use App\Core\Validator;
use App\Models\Account;
use App\Models\AuditLog;
use App\Models\Contract;
use App\Models\Opportunity;
use App\Models\Signature;
use App\Models\User;
use App\Services\CommerceService;
use App\Services\DocumentStore;
use App\Services\RecordService;

final class ContractController extends BaseController
{
    public function index(Request $request): void
    {
        $this->requireModule('contracts');
        $status = (string) ($_GET['status'] ?? 'active');
        if (!in_array($status, ['all', 'draft', 'active', 'renewing', 'expired', 'cancelled'], true)) {
            $status = 'active';
        }
        $page = $this->page();
        $scope = $this->ownerScope();
        $assigned = (int) ($_GET['assigned'] ?? 0);
        $ownerFilter = $scope;
        if ($scope === null && $assigned > 0) {
            $ownerFilter = $assigned;
        }
        $accountId = (int) ($_GET['account'] ?? 0);
        $renewal = (string) ($_GET['renewal'] ?? 'all');
        if (!in_array($renewal, ['all', 'week', 'month', 'overdue'], true)) {
            $renewal = 'all';
        }
        $signature = (string) ($_GET['signature'] ?? 'all');
        if (!in_array($signature, ['all', 'pending', 'signed', 'none'], true)) {
            $signature = 'all';
        }
        $result = Contract::paginate($ownerFilter, $status, $this->q(), $page, 20, $accountId, $renewal, $signature);
        $draft = [];
        $renewFrom = null;
        $fromId = (int) ($_GET['renew_from'] ?? 0);
        if ($fromId > 0) {
            $src = Contract::detail($fromId);
            if ($src && (!$scope || (int) $src['owner_id'] === $scope)) {
                $renewFrom = $src;
                $start = substr((string) ($src['renewal_date'] ?: $src['end_date'] ?: date('Y-m-d')), 0, 10);
                $oldStart = strtotime((string) ($src['start_date'] ?? ''));
                $oldEnd = strtotime((string) ($src['end_date'] ?? ''));
                $span = ($oldStart && $oldEnd && $oldEnd > $oldStart)
                    ? max(1, (int) round(($oldEnd - $oldStart) / 86400))
                    : 365;
                $end = date('Y-m-d', strtotime($start . ' +' . $span . ' days'));
                $title = (string) $src['title'];
                if (!str_starts_with($title, 'Renewal: ')) {
                    $title = 'Renewal: ' . $title;
                }
                $draft = [
                    'account_id' => (int) $src['account_id'],
                    'title' => $title,
                    'amount' => (string) $src['amount'],
                    'start_date' => $start,
                    'end_date' => $end,
                    'renewal_date' => $end,
                    'renewed_from_id' => (int) $src['id'],
                ];
            }
        }
        view('contracts/index', [
            'title' => 'Contracts',
            'rows' => $result['rows'],
            'total' => $result['total'],
            'page' => $page,
            'pages' => max(1, (int) ceil($result['total'] / 20)),
            'q' => $this->q(),
            'status' => $status,
            'assigned' => $assigned,
            'showAssigned' => $scope === null,
            'accountId' => $accountId,
            'renewal' => $renewal,
            'signature' => $signature,
            'counts' => Contract::counts($ownerFilter),
            'accounts' => Account::allForSelect(),
            'owners' => User::salespeople((int) Auth::instance()->tenantId()),
            'draft' => $draft,
            'renewFrom' => $renewFrom,
            'notifications' => $this->notifications(),
        ]);
    }

    public function store(Request $request): void
    {
        $this->requireModule('contracts');
        $this->verifyCsrf($request);
        $data = $request->all();
        $v = (new Validator())->required($data, ['account_id' => 'Account', 'title' => 'Title']);
        if ($v->fails()) {
            $this->fail($v);
        }
        $account = Account::find((int) $data['account_id']);
        if (!$account) {
            json_out(['ok' => false, 'error' => 'Account not found.'], 404);
        }
        $file = $_FILES['pdf'] ?? [];
        try {
            $fileName = DocumentStore::accept(is_array($file) ? $file : [], 'Upload a PDF for this contract.');
        } catch (\RuntimeException $e) {
            json_out(['ok' => false, 'error' => $e->getMessage()], 422);
        }
        $oppId = !empty($data['opportunity_id']) ? (int) $data['opportunity_id'] : null;
        if ($oppId && !Opportunity::find($oppId)) {
            $oppId = null;
        }
        $start = $data['start_date'] ?: date('Y-m-d');
        $end = $data['end_date'] ?: date('Y-m-d', strtotime($start . ' +1 year'));
        $renew = $data['renewal_date'] ?: $end;
        $fromId = (int) ($data['renewed_from_id'] ?? $_GET['renew_from'] ?? 0);
        if ($fromId > 0) {
            $src = Contract::find($fromId);
            if (!$src) {
                $fromId = 0;
            }
        }
        $row = [
            'account_id' => (int) $account['id'],
            'opportunity_id' => $oppId,
            'owner_id' => (int) Auth::instance()->id(),
            'number' => CommerceService::tempNumber(),
            'title' => $data['title'],
            'amount' => (float) ($data['amount'] ?? 0),
            'start_date' => $start,
            'end_date' => $end,
            'renewal_date' => $renew,
            'status' => in_array((string) ($data['status'] ?? 'active'), ['draft', 'active', 'expired', 'cancelled'], true)
                ? (string) ($data['status'] ?? 'active')
                : 'active',
            'file_name' => $fileName,
        ];
        if ($fromId > 0) {
            $row['renewed_from_id'] = $fromId;
        }
        $id = Contract::create($row);
        try {
            DocumentStore::saveEntity('contract', $id, (string) $file['tmp_name']);
        } catch (\RuntimeException $e) {
            json_out(['ok' => false, 'error' => $e->getMessage()], 500);
        }
        CommerceService::assignNumber('contracts', $id, 'CT-');
        RecordService::logActivity('note', 'Contract saved', (string) $data['title'], 'account', (int) $account['id']);
        AuditLog::write('contract.create', 'contract', $id);
        if ($fromId > 0) {
            $created = Contract::find($id);
            if ($created) {
                try {
                    CommerceService::shareContract($created);
                } catch (\RuntimeException $e) {
                    json_out(['ok' => false, 'error' => $e->getMessage()], 422);
                }
            }
        }
        json_out(['ok' => true, 'id' => $id, 'redirect' => $fromId > 0 ? url('renewals') : url('contracts/' . $id)]);
    }

    public function show(Request $request): void
    {
        $this->requireModule('contracts');
        $row = Contract::detail((int) $request->param('id'));
        if (!$row) {
            flash('error', 'Contract not found.');
            redirect('contracts');
        }
        view('contracts/show', [
            'title' => $row['number'],
            'row' => $row,
            'sign' => Signature::forRelated('contract', (int) $row['id']),
            'notifications' => $this->notifications(),
        ]);
    }

    public function esign(Request $request): void
    {
        $this->requireModule('contracts');
        $this->verifyCsrf($request);
        $row = Contract::find((int) $request->param('id'));
        if (!$row) {
            json_out(['ok' => false, 'error' => 'Not found.'], 404);
        }
        $existing = Signature::forRelated('contract', (int) $row['id']);
        if ($existing && ($existing['status'] ?? '') === 'signed') {
            json_out(['ok' => false, 'error' => 'This contract is already signed.'], 422);
        }
        try {
            CommerceService::requestSignature(
                (int) $row['account_id'],
                'Sign contract ' . $row['number'],
                (string) $row['title'],
                'contract',
                (int) $row['id']
            );
        } catch (\RuntimeException $e) {
            json_out(['ok' => false, 'error' => $e->getMessage()], 422);
        }
        AuditLog::write('contract.esign', 'contract', (int) $row['id']);
        json_out(['ok' => true, 'message' => 'Signature request sent to the customer.']);
    }

    public function file(Request $request): void
    {
        $this->requireModule('contracts');
        $row = Contract::find((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']);
    }
}
