<?php

declare(strict_types=1);

final class CustomerReportsService
{
    public const TYPE_IPM_MAP = 'ipm_map';
    public const TYPE_GAP_ANALYSIS = 'gap_analysis_report';
    public const TYPE_TREND_ANALYSIS = 'trend_analysis_report';

    public static function publicUrl(string $storedPath): string
    {
        $p = trim($storedPath);
        if ($p === '') {
            return '';
        }

        if (str_starts_with($p, Constants::ASSET_ROOT . '/')) {
            return $p;
        }

        if (str_starts_with($p, Constants::APP_ROOT . '/')) {
            return $p;
        }

        if (str_starts_with($p, '/uploads/')) {
            $assetAbs = realpath(__DIR__ . '/../../assets') ?: (__DIR__ . '/../../assets');
            $publicAbs = realpath(__DIR__ . '/../../public') ?: (__DIR__ . '/../../public');

            $assetCandidate = rtrim($assetAbs, '/\\') . $p;
            if (is_file($assetCandidate)) {
                return Constants::ASSET_ROOT . $p;
            }

            $publicCandidate = rtrim($publicAbs, '/\\') . $p;
            if (is_file($publicCandidate)) {
                return Constants::APP_ROOT . $p;
            }

            return Constants::ASSET_ROOT . $p;
        }

        return $p;
    }

    public static function ensureTable(PDO $pdo): void
    {
        $pdo->exec(
            "CREATE TABLE IF NOT EXISTS customer_reports (
                report_id INT AUTO_INCREMENT PRIMARY KEY,
                customer_id INT NOT NULL,
                report_type VARCHAR(50) NOT NULL,
                report_month DATE NULL,
                file_path VARCHAR(255) NOT NULL,
                mime_type VARCHAR(100) NOT NULL,
                uploaded_by INT NULL,
                uploaded_at DATETIME NOT NULL,
                INDEX idx_customer_type_month (customer_id, report_type, report_month),
                INDEX idx_customer_type_uploaded (customer_id, report_type, uploaded_at)
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"
        );
    }

    public static function typeLabel(string $type): string
    {
        switch ($type) {
            case self::TYPE_IPM_MAP:
                return 'IPM Map';
            case self::TYPE_GAP_ANALYSIS:
                return 'Gap Analysis Report';
            case self::TYPE_TREND_ANALYSIS:
                return 'Trend Analysis Report';
            default:
                return $type;
        }
    }

    public static function normalizeMonth(?string $ym): ?string
    {
        $ym = trim((string)$ym);
        if ($ym === '') {
            return null;
        }

        if (!preg_match('/^\d{4}-\d{2}$/', $ym)) {
            return null;
        }

        return $ym . '-01';
    }

    public static function allowedMimeToExt(string $mime): ?string
    {
        $allowed = [
            'application/pdf' => 'pdf',
            'image/jpeg' => 'jpg',
            'image/png' => 'png',
            'image/webp' => 'webp',
            'image/gif' => 'gif',
        ];

        return $allowed[$mime] ?? null;
    }

    public static function canUpload(PDO $pdo, int $customerId, string $type, ?string $reportMonth): array
    {
        self::ensureTable($pdo);

        if ($customerId <= 0) {
            return [false, 'Invalid customer.'];
        }

        if (!in_array($type, [self::TYPE_IPM_MAP, self::TYPE_GAP_ANALYSIS, self::TYPE_TREND_ANALYSIS], true)) {
            return [false, 'Invalid report type.'];
        }

        if ($type === self::TYPE_IPM_MAP) {
            return [true, ''];
        }

        if ($reportMonth === null) {
            return [false, 'Please select a month for this report.'];
        }

        $stmt = $pdo->prepare(
            'SELECT COUNT(*) FROM customer_reports WHERE customer_id = :cid AND report_type = :t AND report_month = :m'
        );
        $stmt->execute(['cid' => $customerId, 't' => $type, 'm' => $reportMonth]);
        $exists = (int)$stmt->fetchColumn() > 0;
        if ($exists) {
            return [false, 'A report for this month already exists for this customer.'];
        }

        return [true, ''];
    }

    public static function listReports(PDO $pdo, int $customerId, string $type): array
    {
        self::ensureTable($pdo);

        $stmt = $pdo->prepare(
            'SELECT report_id, customer_id, report_type, report_month, file_path, mime_type, uploaded_by, uploaded_at
             FROM customer_reports
             WHERE customer_id = :cid AND report_type = :t
             ORDER BY COALESCE(report_month, DATE(uploaded_at)) DESC, uploaded_at DESC, report_id DESC'
        );
        $stmt->execute(['cid' => $customerId, 't' => $type]);
        return $stmt->fetchAll();
    }

    public static function saveUpload(PDO $pdo, int $customerId, string $type, ?string $reportMonth, array $file, ?int $uploadedBy): array
    {
        self::ensureTable($pdo);

        $reportMonth = ($type === self::TYPE_IPM_MAP) ? null : $reportMonth;
        [$ok, $msg] = self::canUpload($pdo, $customerId, $type, $reportMonth);
        if (!$ok) {
            return [false, $msg, null];
        }

        if (!isset($file['tmp_name'], $file['error'], $file['size'])) {
            return [false, 'Invalid upload.', null];
        }

        if ((int)$file['error'] !== UPLOAD_ERR_OK) {
            return [false, 'File upload failed.', null];
        }

        $maxBytes = 12 * 1024 * 1024;
        if ((int)$file['size'] <= 0 || (int)$file['size'] > $maxBytes) {
            return [false, 'File must be less than 12MB.', null];
        }

        $finfo = new finfo(FILEINFO_MIME_TYPE);
        $mime = (string)$finfo->file((string)$file['tmp_name']);
        $ext = self::allowedMimeToExt($mime);
        if ($ext === null) {
            return [false, 'Only PDF or image files (JPG, PNG, WEBP, GIF) are allowed.', null];
        }

        $uploadsRoot = realpath(__DIR__ . '/../../assets') ?: (__DIR__ . '/../../assets');
        $relDir = 'uploads/customer_reports/' . $customerId . '/' . $type;
        $absDir = rtrim($uploadsRoot, '/\\') . '/' . $relDir;

        if (!is_dir($absDir)) {
            if (!mkdir($absDir, 0775, true) && !is_dir($absDir)) {
                return [false, 'Failed to create upload directory.', null];
            }
        }

        $prefix = $reportMonth ? str_replace('-', '', substr($reportMonth, 0, 7)) . '_' : '';
        $safeName = $prefix . bin2hex(random_bytes(16)) . '.' . $ext;

        $absPath = $absDir . '/' . $safeName;
        $relPath = Constants::ASSET_ROOT . '/' . $relDir . '/' . $safeName;

        if (!move_uploaded_file((string)$file['tmp_name'], $absPath)) {
            return [false, 'Failed to save uploaded file.', null];
        }

        $stmt = $pdo->prepare(
            'INSERT INTO customer_reports (customer_id, report_type, report_month, file_path, mime_type, uploaded_by, uploaded_at)
             VALUES (:cid, :t, :m, :p, :mime, :by, :at)'
        );

        $stmt->execute([
            'cid' => $customerId,
            't' => $type,
            'm' => $reportMonth,
            'p' => $relPath,
            'mime' => $mime,
            'by' => $uploadedBy,
            'at' => date('Y-m-d H:i:s'),
        ]);

        return [true, 'Uploaded successfully.', $relPath];
    }
}
