<?php
/**
 * TenderFmVerificationAttachments · Slice 8Z-q.
 *
 * FM-owned documents attached to a Won tender's contract-approval flow — kept in
 * a separate table from tender_submission_attachments so:
 *   · the subtype enum is FM-specific (cover memo, cash-flow, profitability model…)
 *   · the mandatory-3 gate on Tenders::submitContractForApproval() has a clean check
 *   · CRM's submission proofs don't get mixed with FM's decision memos on the same list
 *
 * Files land under ROOT/uploads/tenders/{tenderId}/fm-verify/.
 */
class TenderFmVerificationAttachments extends Model
{
    /** Labels + mandatory flag for the sub-types shown in the UI dropdown. */
    public const SUBTYPES = [
        'COVER_MEMO'               => ['label' => 'FM cover memo',            'mandatory' => true],
        'CASH_FLOW'                => ['label' => 'Cash-flow projection',     'mandatory' => true],
        'PROFITABILITY_MODEL'      => ['label' => 'Profitability model',      'mandatory' => true],
        'BANK_FACILITY_LETTER'     => ['label' => 'Bank facility letter',     'mandatory' => false],
        'CREDIT_ASSESSMENT'        => ['label' => 'Client credit assessment', 'mandatory' => false],
        'FX_HEDGING_MEMO'          => ['label' => 'FX hedging memo',          'mandatory' => false],
        'INSURANCE_CHECKLIST'      => ['label' => 'Insurance checklist',      'mandatory' => false],
        'RELATED_PARTY_DISCLOSURE' => ['label' => 'Related-party disclosure', 'mandatory' => false],
        'OTHER'                    => ['label' => 'Other',                    'mandatory' => false],
    ];

    public const MAX_BYTES = 10 * 1024 * 1024;

    public const ALLOWED_MIMES = [
        'application/pdf',
        'image/jpeg', 'image/png', 'image/webp', 'image/heic', 'image/gif',
        'application/msword',
        'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
        'application/vnd.ms-excel',
        'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
        'application/zip', 'application/x-zip-compressed',
        'text/plain',
    ];

    public function listFor(int $tenderId): array
    {
        $db = new DB();
        return $db->mySelect(
            "SELECT * FROM tender_fm_verification_attachments
              WHERE tender_id = :t AND deleted = 0
              ORDER BY uploaded_on DESC, c0d DESC",
            [':t' => $tenderId]
        ) ?: [];
    }

    public function find(int $id): ?array
    {
        $db = new DB();
        $r = $db->mySelect("SELECT * FROM tender_fm_verification_attachments WHERE c0d = :id AND deleted = 0", [':id' => $id]);
        return $r[0] ?? null;
    }

    public function upload(int $tenderId, array $file, array $meta, int $userId, string $userName): array
    {
        if (empty($file) || !isset($file['tmp_name']) || $file['error'] !== UPLOAD_ERR_OK) {
            return ['ok' => false, 'msg' => 'No file received (upload error ' . ($file['error'] ?? '?') . ').'];
        }
        $size = (int)$file['size'];
        if ($size <= 0) return ['ok' => false, 'msg' => 'File is empty.'];
        if ($size > self::MAX_BYTES) return ['ok' => false, 'msg' => 'File too large (max ' . (self::MAX_BYTES / 1024 / 1024) . ' MB).'];

        $probed = null;
        if (function_exists('finfo_open')) {
            $fi = finfo_open(FILEINFO_MIME_TYPE);
            if ($fi) { $probed = finfo_file($fi, $file['tmp_name']) ?: null; finfo_close($fi); }
        }
        $mime = $probed ?: (string)($file['type'] ?? '');
        if (!in_array($mime, self::ALLOWED_MIMES, true)) {
            return ['ok' => false, 'msg' => 'File type "' . htmlspecialchars($mime) . '" not allowed.'];
        }

        $subtype = strtoupper((string)($meta['subtype'] ?? 'OTHER'));
        if (!isset(self::SUBTYPES[$subtype])) $subtype = 'OTHER';
        $label = trim((string)($meta['label'] ?? '')) ?: (string)self::SUBTYPES[$subtype]['label'];

        $origName = basename((string)$file['name']);
        $safe = preg_replace('/[^A-Za-z0-9._-]+/', '_', $origName);
        if (strlen($safe) > 120) $safe = substr($safe, -120);
        $dir = ROOT . '/uploads/tenders/' . $tenderId . '/fm-verify';
        if (!is_dir($dir) && !@mkdir($dir, 0775, true)) return ['ok' => false, 'msg' => 'Server could not create upload directory.'];
        $fname = uniqid('fm', true) . '_' . $safe;
        $abs   = $dir . '/' . $fname;
        $rel   = 'uploads/tenders/' . $tenderId . '/fm-verify/' . $fname;
        if (!@move_uploaded_file($file['tmp_name'], $abs)) return ['ok' => false, 'msg' => 'Server could not save the file.'];

        $db = new DB();
        $r = $db->insertUpdate(
            "INSERT INTO tender_fm_verification_attachments
                (tender_id, subtype, label, original_filename, stored_path, mime_type, size_bytes, notes,
                 uploaded_by, uploaded_by_name, uploaded_on)
             VALUES (:t, :st, :l, :of, :sp, :m, :sz, :n, :u, :un, NOW())",
            [
                ':t' => $tenderId, ':st' => $subtype, ':l' => $label,
                ':of' => $origName, ':sp' => $rel, ':m' => $mime, ':sz' => $size,
                ':n' => (string)($meta['notes'] ?? ''),
                ':u' => $userId, ':un' => $userName,
            ]
        );
        return ['ok' => true, 'id' => (int)($r['last_id'] ?? 0)];
    }

    public function remove(int $id, int $userId): array
    {
        $cur = $this->find($id);
        if (!$cur) return ['ok' => false, 'msg' => 'Attachment not found.'];
        $db = new DB();
        $db->insertUpdate(
            "UPDATE tender_fm_verification_attachments SET deleted = 1, uploaded_by = :u WHERE c0d = :id",
            [':u' => $userId, ':id' => $id]
        );
        $abs = ROOT . '/' . ltrim((string)$cur['stored_path'], '/');
        if (is_file($abs)) @unlink($abs);
        return ['ok' => true];
    }

    public function absolutePath(array $row): ?string
    {
        $abs = ROOT . '/' . ltrim((string)$row['stored_path'], '/');
        return is_file($abs) ? $abs : null;
    }

    /**
     * The mandatory-3 gate used by Tenders::submitContractForApproval() to
     * block chain submission when FM hasn't produced the decision memos yet.
     *
     * Returns:
     *   [
     *     'ok'         => bool                — true when every mandatory subtype has ≥ 1 upload
     *     'missing'    => ['COVER_MEMO', ...] — enum keys of missing mandatory subtypes
     *     'labels'     => ['FM cover memo',…] — human labels for the "missing" set
     *   ]
     */
    public function mandatoryCheck(int $tenderId): array
    {
        $rows = $this->listFor($tenderId);
        $present = [];
        foreach ($rows as $r) $present[(string)$r['subtype']] = true;
        $missing = [];
        $labels  = [];
        foreach (self::SUBTYPES as $k => $meta) {
            if (empty($meta['mandatory'])) continue;
            if (empty($present[$k])) {
                $missing[] = $k;
                $labels[]  = $meta['label'];
            }
        }
        return ['ok' => empty($missing), 'missing' => $missing, 'labels' => $labels];
    }
}
