<?php

require_once BASE_PATH . '/app/controllers/owner/OwnerBaseController.php';
require_once BASE_PATH . '/app/models/Report.php';

class OwnerReportController extends OwnerBaseController
{
    private const LOW_STOCK_THRESHOLD = 5;

    private Report $reportModel;

    public function __construct()
    {
        $this->requireOwner();
        $this->reportModel = new Report();
    }

    public function index(): void
    {
        $this->requireGet();
        $range = $this->resolveDateRange();
        $currency = $this->currency();

        $completedSales = $this->emptyCompletedSales();
        $lifecycle = $this->emptyLifecycle();
        $dailyRevenue = [];
        $monthlyRevenue = [];
        $bestSellingProducts = [];
        $categoryPerformance = [];
        $rawStatusStats = [];
        $quantitySold = 0;

        if ($range['is_valid']) {
            $completedSales = $this->reportModel->getCompletedSalesSummary(
                $range['from_start'],
                $range['to_exclusive']
            );
            $lifecycle = $this->reportModel->getOrderLifecycleSummary(
                $range['from_start'],
                $range['to_exclusive']
            );
            $dailyRevenue = $this->reportModel->getRevenueByDay(
                $range['from_start'],
                $range['to_exclusive']
            );
            $monthlyRevenue = $this->reportModel->getRevenueByMonth(
                $range['from_start'],
                $range['to_exclusive']
            );
            $rawStatusStats = $this->reportModel->getOrderStatusStats(
                $range['from_start'],
                $range['to_exclusive']
            );
            $quantitySold = $this->reportModel->getCompletedQuantitySold(
                $range['from_start'],
                $range['to_exclusive']
            );
            $bestSellingProducts = $this->reportModel->getBestSellingProducts(
                $range['from_start'],
                $range['to_exclusive'],
                10
            );
            $categoryPerformance = $this->reportModel->getCategoryPerformance(
                $range['from_start'],
                $range['to_exclusive']
            );
        }

        $statusStats = $this->normalizeStatusStats(
            $rawStatusStats,
            (int) $lifecycle['total_orders']
        );
        $cancellationRate = (int) $lifecycle['total_orders'] > 0
            ? ((int) $lifecycle['cancelled_order_count']
                / (int) $lifecycle['total_orders']) * 100
            : 0;

        require BASE_PATH . '/app/views/owner/reports/index.php';
    }

    public function products(): void
    {
        $this->requireGet();
        $range = $this->resolveDateRange();
        $currency = $this->currency();
        $search = $this->readSearch();
        $sort = $this->readProductSort();
        $page = max(1, (int) ($_GET['page'] ?? 1));
        $perPage = 25;
        $totalProducts = 0;
        $totalPages = 1;
        $productSales = [];

        if ($range['is_valid']) {
            $totalProducts = $this->reportModel->countProductSales(
                $range['from_start'],
                $range['to_exclusive'],
                $search
            );
            $totalPages = max(1, (int) ceil($totalProducts / $perPage));
            $page = min($page, $totalPages);
            $productSales = $this->reportModel->getProductSales(
                $range['from_start'],
                $range['to_exclusive'],
                $search,
                $sort,
                $perPage,
                ($page - 1) * $perPage
            );
        }

        require BASE_PATH . '/app/views/owner/reports/products.php';
    }

    public function customers(): void
    {
        $this->requireGet();
        $range = $this->resolveDateRange();
        $currency = $this->currency();
        $search = $this->readSearch();
        $page = max(1, (int) ($_GET['page'] ?? 1));
        $perPage = 25;
        $totalCustomers = 0;
        $totalPages = 1;
        $topCustomers = [];

        if ($range['is_valid']) {
            $totalCustomers = $this->reportModel->countTopCustomers(
                $range['from_start'],
                $range['to_exclusive'],
                $search
            );
            $totalPages = max(1, (int) ceil($totalCustomers / $perPage));
            $page = min($page, $totalPages);
            $topCustomers = $this->reportModel->getTopCustomers(
                $range['from_start'],
                $range['to_exclusive'],
                $search,
                $perPage,
                ($page - 1) * $perPage
            );
        }

        require BASE_PATH . '/app/views/owner/reports/customers.php';
    }

    public function promotions(): void
    {
        $this->requireGet();
        $range = $this->resolveDateRange();
        $currency = $this->currency();
        $promotionPerformance = $range['is_valid']
            ? $this->reportModel->getPromotionPerformance(
                $range['from_start'],
                $range['to_exclusive']
            )
            : [];

        require BASE_PATH . '/app/views/owner/reports/promotions.php';
    }

    public function inventory(): void
    {
        $this->requireGet();
        $currency = $this->currency();
        $lowStockThreshold = self::LOW_STOCK_THRESHOLD;
        $lowStockProducts = $this->reportModel->getLowStockProducts(
            $lowStockThreshold
        );
        $outOfStockProducts = $this->reportModel->getOutOfStockProducts();

        require BASE_PATH . '/app/views/owner/reports/inventory.php';
    }

    public function exportProducts(): never
    {
        $this->requireGet();
        $range = $this->requireExportRange();
        $search = $this->readSearch();
        $sort = $this->readProductSort();
        $rows = $this->reportModel->getProductSales(
            $range['from_start'],
            $range['to_exclusive'],
            $search,
            $sort,
            5000,
            0
        );

        $this->sendCsv(
            'bao-cao-san-pham-' . date('Ymd') . '.csv',
            [
                'Product ID',
                'SKU',
                'Tên sản phẩm',
                'Số lượng đã bán',
                'Số đơn hoàn thành',
                'Doanh thu'
            ],
            array_map(
                static fn (array $row): array => [
                    $row['product_id'],
                    $row['sku'],
                    $row['product_name'],
                    $row['quantity_sold'],
                    $row['order_count'],
                    $row['revenue']
                ],
                $rows
            )
        );
    }

    public function exportCustomers(): never
    {
        $this->requireGet();
        $range = $this->requireExportRange();
        $search = $this->readSearch();
        $rows = $this->reportModel->getTopCustomers(
            $range['from_start'],
            $range['to_exclusive'],
            $search,
            5000,
            0
        );

        $this->sendCsv(
            'bao-cao-khach-hang-' . date('Ymd') . '.csv',
            [
                'Customer ID',
                'Họ tên',
                'Số điện thoại',
                'Email',
                'Số đơn hoàn thành',
                'Tổng chi tiêu',
                'Giá trị đơn trung bình',
                'Đơn hoàn thành gần nhất'
            ],
            array_map(
                static fn (array $row): array => [
                    $row['customer_id'],
                    $row['full_name'],
                    $row['phone'],
                    $row['email'],
                    $row['completed_order_count'],
                    $row['total_spent'],
                    $row['average_order_value'],
                    $row['last_completed_at']
                ],
                $rows
            )
        );
    }

    public function exportRevenue(): never
    {
        $this->requireGet();
        $range = $this->requireExportRange();
        $rows = $this->reportModel->getRevenueByDay(
            $range['from_start'],
            $range['to_exclusive']
        );

        $this->sendCsv(
            'doanh-thu-theo-ngay-' . date('Ymd') . '.csv',
            ['Ngày hoàn thành', 'Số đơn hoàn thành', 'Doanh thu'],
            array_map(
                static fn (array $row): array => [
                    $row['report_date'],
                    $row['order_count'],
                    $row['revenue']
                ],
                $rows
            )
        );
    }

    private function resolveDateRange(): array
    {
        $today = new DateTimeImmutable('today');
        $rangeKey = trim((string) ($_GET['range'] ?? ''));
        $fromInput = trim((string) ($_GET['from'] ?? ''));
        $toInput = trim((string) ($_GET['to'] ?? ''));

        if ($rangeKey === '') {
            $rangeKey = ($fromInput !== '' || $toInput !== '')
                ? 'custom'
                : '30days';
        }

        $ranges = [
            'today' => [$today, $today],
            '7days' => [$today->modify('-6 days'), $today],
            '30days' => [$today->modify('-29 days'), $today],
            'this_month' => [$today->modify('first day of this month'), $today],
            'last_month' => [
                $today->modify('first day of last month'),
                $today->modify('last day of last month')
            ],
            'this_year' => [
                $today->setDate((int) $today->format('Y'), 1, 1),
                $today
            ]
        ];

        if ($rangeKey === 'custom') {
            $from = $this->parseDate($fromInput);
            $to = $this->parseDate($toInput);

            if (!$from || !$to || $from > $to) {
                return [
                    'is_valid' => false,
                    'key' => 'custom',
                    'from' => $fromInput,
                    'to' => $toInput,
                    'from_start' => '',
                    'to_exclusive' => '',
                    'label' => 'Khoảng ngày không hợp lệ',
                    'error' => 'Vui lòng nhập khoảng ngày hợp lệ, với ngày bắt đầu không sau ngày kết thúc.'
                ];
            }
        } elseif (isset($ranges[$rangeKey])) {
            [$from, $to] = $ranges[$rangeKey];
        } else {
            [$from, $to] = $ranges['30days'];
            $rangeKey = '30days';
        }

        return [
            'is_valid' => true,
            'key' => $rangeKey,
            'from' => $from->format('Y-m-d'),
            'to' => $to->format('Y-m-d'),
            'from_start' => $from->format('Y-m-d 00:00:00'),
            'to_exclusive' => $to->modify('+1 day')->format('Y-m-d 00:00:00'),
            'label' => $from->format('d/m/Y') . ' - ' . $to->format('d/m/Y'),
            'error' => null
        ];
    }

    private function parseDate(string $date): ?DateTimeImmutable
    {
        if ($date === '') {
            return null;
        }

        $parsed = DateTimeImmutable::createFromFormat('!Y-m-d', $date);
        $errors = DateTimeImmutable::getLastErrors();

        if ($parsed === false || ($errors !== false
            && ($errors['warning_count'] > 0 || $errors['error_count'] > 0))) {
            return null;
        }

        return $parsed;
    }

    private function normalizeStatusStats(array $rows, int $totalOrders): array
    {
        $labels = [
            'pending' => 'Chờ xác nhận',
            'confirmed' => 'Đã xác nhận',
            'packing' => 'Đang đóng gói',
            'shipping' => 'Đang giao',
            'completed' => 'Hoàn thành',
            'cancelled' => 'Đã hủy'
        ];
        $counts = array_fill_keys(array_keys($labels), 0);

        foreach ($rows as $row) {
            if (isset($counts[$row['status']])) {
                $counts[$row['status']] = (int) $row['order_count'];
            }
        }

        $stats = [];
        foreach ($labels as $status => $label) {
            $count = $counts[$status];
            $stats[] = [
                'status' => $status,
                'label' => $label,
                'order_count' => $count,
                'percentage' => $totalOrders > 0
                    ? ($count / $totalOrders) * 100
                    : 0
            ];
        }

        return $stats;
    }

    private function readSearch(): string
    {
        return mb_substr(trim((string) ($_GET['search'] ?? '')), 0, 120);
    }

    private function readProductSort(): string
    {
        $sort = trim((string) ($_GET['sort'] ?? 'quantity'));

        return in_array($sort, ['quantity', 'revenue', 'name'], true)
            ? $sort
            : 'quantity';
    }

    private function currency(): string
    {
        return (string) ($GLOBALS['shop_settings']['currency'] ?? 'VND');
    }

    private function requireGet(): void
    {
        if (($_SERVER['REQUEST_METHOD'] ?? 'GET') !== 'GET') {
            http_response_code(405);
            exit('Method Not Allowed');
        }
    }

    private function requireExportRange(): array
    {
        $range = $this->resolveDateRange();

        if (!$range['is_valid']) {
            http_response_code(422);
            exit('Khoảng ngày không hợp lệ.');
        }

        return $range;
    }

    private function sendCsv(string $filename, array $header, array $rows): never
    {
        header('Content-Type: text/csv; charset=UTF-8');
        header('Content-Disposition: attachment; filename="' . $filename . '"');
        header('X-Content-Type-Options: nosniff');

        $output = fopen('php://output', 'wb');
        echo "\xEF\xBB\xBF";
        fputcsv($output, $header);

        foreach ($rows as $row) {
            fputcsv(
                $output,
                array_map([$this, 'sanitizeCsvCell'], $row)
            );
        }

        fclose($output);
        exit;
    }

    private function sanitizeCsvCell(mixed $value): string|int|float
    {
        if (is_int($value) || is_float($value)) {
            return $value;
        }

        $value = (string) ($value ?? '');

        return preg_match('/^[=+\-@]/', $value)
            ? "'" . $value
            : $value;
    }

    private function emptyCompletedSales(): array
    {
        return [
            'completed_order_count' => 0,
            'final_revenue' => 0,
            'average_order_value' => 0,
            'subtotal_amount' => 0,
            'discount_amount' => 0,
            'shipping_amount' => 0,
            'customer_count' => 0
        ];
    }

    private function emptyLifecycle(): array
    {
        return [
            'total_orders' => 0,
            'cancelled_order_count' => 0,
            'cancelled_order_value' => 0
        ];
    }
}
