<?php

namespace App\Services;

use App\Support\AnalysisResultCache;
use App\Support\ExcelStreamReader;
use Illuminate\Support\Facades\Storage;

class LiveAnalysisService
{
    private const UPLOAD_DIR = 'private_uploads';

    /**
     * Số cột dữ liệu của file danh sách phiên live (A → Z).
     * Các cột AA..AD trống không nằm trong nhóm dữ liệu cần đọc.
     */
    private const DATA_COL_COUNT = 26;

    /**
     * Tên cột tiêu chuẩn của file Transaction Analysis Live List.
     * Dùng cho cả tính KPI tổng hợp lẫn breakdown theo creator/sản phẩm/khung giờ/ngày.
     */
    private const C_TITLE = 'Tiêu đề buổi LIVE';
    private const C_LIVE_ID = 'ID buổi LIVE';
    private const C_START = 'Thời gian bắt đầu LIVE';
    private const C_END = 'Thời gian kết thúc LIVE';
    private const C_CREATOR = 'Tên nhà sáng tạo';
    private const C_PRODUCT = 'ID sản phẩm';
    private const C_GMV = 'GMV nhờ buổi LIVE của nhà sáng tạo';
    private const C_ITEMS = 'Số món bán ra ghi nhận vào buổi LIVE';
    private const C_REFUND = 'Hoàn tiền';
    private const C_ORDERS = 'Đơn hàng nhờ buổi LIVE';
    private const C_AOV = 'AOV';
    private const C_IMP = 'Lượt hiển thị';
    private const C_COMMISSION = 'Hoa hồng ước tính';
    private const C_PRODUCT_IMP = 'Lượt hiển thị sản phẩm trong LIVE';
    private const C_PRODUCT_CLICKS = 'Lượt nhấp vào sản phẩm';
    private const C_PRODUCT_VIEWERS = 'Người xem sản phẩm trong buổi LIVE';
    private const C_LIKES = 'Lượt thích';
    private const C_COMMENTS = 'Lượt bình luận';
    private const C_SHARES = 'Lượt chia sẻ';
    private const C_GPM = 'GPM hiển thị';
    private const C_AVG_VIEW = 'Thời lượng xem trung bình';
    private const C_ENGAGEMENT = 'Tương tác';

    public function storeUpload($file)
    {
        $filename = 'live_analysis_' . time() . '_' . uniqid() . '.' . $file->getClientOriginalExtension();
        $path = $file->storeAs(self::UPLOAD_DIR, $filename, 'local');
        return $path;
    }

    /**
     * Xử lý file đã upload: parse (stream) → phân tích → ghi cache theo hash
     * nội dung file. Nếu cùng nội dung đã xử lý trước đó, trả luôn kết quả cache.
     *
     * @return array|null kết quả dùng cho controller (headers/total_rows/sheet_name/live_metrics)
     */
    public function processFile($filePath)
    {
        $fullPath = Storage::disk('local')->path($filePath);
        $cached = AnalysisResultCache::get('live', $fullPath);
        if ($cached !== null) {
            return $cached;
        }

        $parsed = $this->parseExcel($filePath);
        $metrics = $this->analyzeLive($parsed);
        $result = [
            'headers' => $parsed['headers'],
            'total_rows' => $metrics['session_count'] ?? 0,
            'sheet_name' => $parsed['sheet_name'],
            'live_metrics' => $metrics,
        ];

        AnalysisResultCache::put('live', $fullPath, $result);

        return $result;
    }

    /**
     * Đọc file Excel danh sách phiên live theo dòng bằng stream
     * (ReadFilter + readDataOnly) để không load toàn bộ workbook vào RAM.
     * Dòng 1 = tiêu đề cột; bỏ qua dòng không có ID buổi LIVE.
     * data trả về là Generator — chỉ giữ 1 dòng trong bộ nhớ tại một thời điểm.
     */
    public function parseExcel($filePath)
    {
        $fullPath = Storage::disk('local')->path($filePath);
        if (!file_exists($fullPath)) {
            throw new \Exception('File không tồn tại.');
        }

        [$spreadsheet, $worksheet] = ExcelStreamReader::open($fullPath, self::DATA_COL_COUNT);
        $sheetName = $worksheet->getTitle();

        // Dòng 1: tiêu đề cột (trim, bỏ cột trống)
        $headerRow = ExcelStreamReader::firstRow($worksheet, self::DATA_COL_COUNT);
        $headers = [];
        foreach ($headerRow as $idx => $header) {
            $header = trim((string) $header);
            if ($header === '') {
                unset($headerRow[$idx]);
            }
        }
        $headers = array_values($headerRow);
        $colCount = count($headers);

        // Dữ liệu từ dòng 2; bỏ qua dòng không có ID buổi LIVE (cột B ≈ index 1)
        $idLiveIndex = 1;
        $data = (function () use ($worksheet, $headers, $colCount, $idLiveIndex) {
            foreach (ExcelStreamReader::rows($worksheet, self::DATA_COL_COUNT, 2) as $rowData) {
                if (!isset($rowData[$idLiveIndex]) || trim((string) $rowData[$idLiveIndex]) === '') {
                    continue;
                }
                $item = [];
                for ($i = 0; $i < $colCount; $i++) {
                    $header = $headers[$i];
                    $value = $rowData[$i] ?? null;
                    $item[$header] = (is_scalar($value) && trim((string) $value) !== '') ? $value : null;
                }
                yield $item;
            }
        })();

        return [
            'headers' => $headers,
            'data' => $data,
            'sheet_name' => $sheetName,
        ];
    }

    /**
     * Tính các KPI tổng hợp trên toàn bộ phiên live trong file.
     * Đồng thời tạo breakdown theo creator/sản phẩm/khung giờ/ngày và chi tiết từng buổi.
     * Dựa theo gợi ý tính toán từ app.js/dashboard.html (chỉ mang tính tham khảo).
     * @return array
     */
    public function analyzeLive($parsed)
    {
        $data = $parsed['data'];
        $rowCount = 0;

        $gmvId = self::C_GMV;
        $ordersId = self::C_ORDERS;
        $refundId = self::C_REFUND;
        $commissionId = self::C_COMMISSION;
        $startId = self::C_START;
        $endId = self::C_END;
        $impId = self::C_IMP;
        $creatorId = self::C_CREATOR;
        $productId = self::C_PRODUCT;
        $titleId = self::C_TITLE;
        $liveIdId = self::C_LIVE_ID;
        $aovId = self::C_AOV;
        $itemsId = self::C_ITEMS;
        $productImpId = self::C_PRODUCT_IMP;
        $productClicksId = self::C_PRODUCT_CLICKS;
        $productViewersId = self::C_PRODUCT_VIEWERS;
        $likesId = self::C_LIKES;
        $commentsId = self::C_COMMENTS;
        $sharesId = self::C_SHARES;
        $gpmId = self::C_GPM;
        $avgViewId = self::C_AVG_VIEW;
        $engagementId = self::C_ENGAGEMENT;

        $gmv = 0;
        $orders = 0;
        $refund = 0;
        $commission = 0;
        $imp = 0;
        $mins = 0;
        $items = 0;
        $productImp = 0;
        $productClicks = 0;
        $productViewers = 0;
        $likes = 0;
        $comments = 0;
        $shares = 0;

        // Breakdown aggregators
        $creatorMap = [];
        $productMap = [];
        $hourMap = [];
        $dayMap = [];
        $details = [];

        foreach ($data as $row) {
            $gmvV = self::parseMoney($row[$gmvId] ?? null);
            $ordersV = self::parseNumber($row[$ordersId] ?? null);
            $refundV = self::parseMoney($row[$refundId] ?? null);
            $commissionV = self::parseMoney($row[$commissionId] ?? null);
            $impV = self::parseNumber($row[$impId] ?? null);
            $itemsV = self::parseNumber($row[$itemsId] ?? null);
            $productImpV = self::parseNumber($row[$productImpId] ?? null);
            $productClicksV = self::parseNumber($row[$productClicksId] ?? null);
            $productViewersV = self::parseNumber($row[$productViewersId] ?? null);
            $likesV = self::parseNumber($row[$likesId] ?? null);
            $commentsV = self::parseNumber($row[$commentsId] ?? null);
            $sharesV = self::parseNumber($row[$sharesId] ?? null);
            $engagementV = self::parseNumber($row[$engagementId] ?? null) / 100;

            $gmv += $gmvV;
            $orders += $ordersV;
            $refund += $refundV;
            $commission += $commissionV;
            $imp += $impV;
            $items += $itemsV;
            $productImp += $productImpV;
            $productClicks += $productClicksV;
            $productViewers += $productViewersV;
            $likes += $likesV;
            $comments += $commentsV;
            $shares += $sharesV;

            $start = self::parseDateRaw($row[$startId] ?? null);
            $end = self::parseDateRaw($row[$endId] ?? null);
            $durMin = 0;
            if ($start && $end) {
                $durMin = max(0, ($end->getTimestamp() - $start->getTimestamp()) / 60);
            }
            $mins += $durMin;

            $creator = trim((string) ($row[$creatorId] ?? ''));
            $creator = $creator !== '' ? $creator : 'Không xác định';
            $creatorMap[$creator]['gmv'] = ($creatorMap[$creator]['gmv'] ?? 0) + $gmvV;
            $creatorMap[$creator]['orders'] = ($creatorMap[$creator]['orders'] ?? 0) + $ordersV;
            $creatorMap[$creator]['refund'] = ($creatorMap[$creator]['refund'] ?? 0) + $refundV;
            $creatorMap[$creator]['commission'] = ($creatorMap[$creator]['commission'] ?? 0) + $commissionV;
            $creatorMap[$creator]['minutes'] = ($creatorMap[$creator]['minutes'] ?? 0) + $durMin;
            $creatorMap[$creator]['sessions'] = ($creatorMap[$creator]['sessions'] ?? 0) + 1;
            $creatorMap[$creator]['imp'] = ($creatorMap[$creator]['imp'] ?? 0) + $impV;
            $creatorMap[$creator]['productImp'] = ($creatorMap[$creator]['productImp'] ?? 0) + $productImpV;
            $creatorMap[$creator]['productClicks'] = ($creatorMap[$creator]['productClicks'] ?? 0) + $productClicksV;
            $creatorMap[$creator]['productViewers'] = ($creatorMap[$creator]['productViewers'] ?? 0) + $productViewersV;
            $creatorMap[$creator]['likes'] = ($creatorMap[$creator]['likes'] ?? 0) + $likesV;
            $creatorMap[$creator]['comments'] = ($creatorMap[$creator]['comments'] ?? 0) + $commentsV;
            $creatorMap[$creator]['shares'] = ($creatorMap[$creator]['shares'] ?? 0) + $sharesV;

            // Ước tính lượt xem phiên = tương tác ÷ tỷ lệ tương tác trong file, dùng cho tỷ lệ TB có trọng số
            $interactionV = $likesV + $commentsV + $sharesV;
            $sessionViews = $engagementV > 0 ? $interactionV / $engagementV : 0;
            $creatorMap[$creator]['engagementViews'] = ($creatorMap[$creator]['engagementViews'] ?? 0) + $sessionViews;

            $product = trim((string) ($row[$productId] ?? ''));
            if ($product !== '') {
                $productMap[$product]['gmv'] = ($productMap[$product]['gmv'] ?? 0) + $gmvV;
                $productMap[$product]['orders'] = ($productMap[$product]['orders'] ?? 0) + $ordersV;
                $productMap[$product]['commission'] = ($productMap[$product]['commission'] ?? 0) + $commissionV;
                $productMap[$product]['sessions'] = ($productMap[$product]['sessions'] ?? 0) + 1;
            }

            if ($start) {
                $hour = (int) $start->format('G');
                $hourMap[$hour]['gmv'] = ($hourMap[$hour]['gmv'] ?? 0) + $gmvV;
                $hourMap[$hour]['orders'] = ($hourMap[$hour]['orders'] ?? 0) + $ordersV;

                $day = $start->format('Y-m-d');
                $dayMap[$day]['gmv'] = ($dayMap[$day]['gmv'] ?? 0) + $gmvV;
                $dayMap[$day]['orders'] = ($dayMap[$day]['orders'] ?? 0) + $ordersV;
                $dayMap[$day]['refund'] = ($dayMap[$day]['refund'] ?? 0) + $refundV;
                $dayMap[$day]['commission'] = ($dayMap[$day]['commission'] ?? 0) + $commissionV;
                $dayMap[$day]['sessions'] = ($dayMap[$day]['sessions'] ?? 0) + 1;
            }

            $details[] = [
                'live_id' => (string) ($row[$liveIdId] ?? ''),
                'title' => (string) ($row[$titleId] ?? ''),
                'creator' => $creator,
                'product_id' => (string) ($row[$productId] ?? ''),
                'start_date' => $start ? $start->format('Y-m-d') : '',
                'start_raw' => $start ? $start->format('d/m/Y H:i') : '',
                'end_raw' => $end ? $end->format('d/m/Y H:i') : '',
                'duration_min' => round($durMin * 10) / 10,
                'gmv' => $gmvV,
                'orders' => $ordersV,
                'aov' => $row[$aovId] ?? null,
                'items' => $itemsV,
                'refund' => $refundV,
                'commission' => $commissionV,
                'impressions' => $impV,
                'product_imp' => $productImpV,
                'product_clicks' => $productClicksV,
                'product_viewers' => $productViewersV,
                'likes' => $likesV,
                'comments' => $commentsV,
                'shares' => $sharesV,
                'engagement' => $engagementV,
                'hour' => $start ? (int) $start->format('G') : null,
                'gmv_hour' => $durMin > 0 ? $gmvV / ($durMin / 60) : 0,
                'gpm' => self::parseMoney($row[$gpmId] ?? null),
                'ctr' => $productImpV > 0 ? $productClicksV / $productImpV : 0,
                'cvr_click' => $productClicksV > 0 ? $ordersV / $productClicksV : 0,
                'refund_rate' => $gmvV > 0 ? $refundV / $gmvV : 0,
                'avg_view' => (string) ($row[$avgViewId] ?? ''),
            ];
            $rowCount++;
        }

        $sessionCount = $rowCount;
        $aov = $orders > 0 ? $gmv / $orders : 0;
        uasort($creatorMap, fn ($a, $b) => $b['gmv'] <=> $a['gmv']);
        $topCreators = [];
        $stt = 1;
        foreach ($creatorMap as $name => $c) {
            $topCreators[] = [
                'stt' => $stt++,
                'creator' => $name,
                'sessions' => $c['sessions'],
                'gmv' => $c['gmv'],
                'orders' => $c['orders'],
                'aov' => $c['orders'] > 0 ? $c['gmv'] / $c['orders'] : 0,
                'commission' => $c['commission'],
                'commission_rate' => $c['gmv'] > 0 ? $c['commission'] / $c['gmv'] : 0,
                'net_gmv' => $c['gmv'] - $c['refund'],
                'gmv_hour' => $c['minutes'] > 0 ? $c['gmv'] / $c['minutes'] * 60 : 0,
                'refund' => $c['refund'],
                'refund_rate' => $c['gmv'] > 0 ? $c['refund'] / $c['gmv'] : 0,
                'hours' => round($c['minutes'] / 60, 1),
            ];
        }

        usort($productMap, fn ($a, $b) => $b['gmv'] <=> $a['gmv']);
        $topProducts = [];
        $stt = 1;
        foreach ($productMap as $pid => $p) {
            $topProducts[] = [
                'stt' => $stt++,
                'product_id' => $pid,
                'sessions' => $p['sessions'],
                'gmv' => $p['gmv'],
                'orders' => $p['orders'],
                'commission' => $p['commission'],
            ];
        }

        ksort($hourMap);
        $hourStats = [];
        foreach ($hourMap as $hour => $h) {
            $hourStats[] = [
                'hour' => $hour,
                'label' => sprintf('%02d:00', $hour),
                'gmv' => $h['gmv'],
                'orders' => $h['orders'],
            ];
        }

        ksort($dayMap);
        $dailyStats = [];
        foreach ($dayMap as $day => $d) {
            $dailyStats[] = [
                'date' => $day,
                'label' => self::formatDayLabel($day),
                'gmv' => $d['gmv'],
                'orders' => $d['orders'],
                'refund' => $d['refund'],
                'commission' => $d['commission'],
                'sessions' => $d['sessions'],
            ];
        }

        $aov = $orders > 0 ? $gmv / $orders : 0;
        $gmvHour = $mins > 0 ? $gmv / ($mins / 60) : 0;
        $refundRate = $gmv > 0 ? $refund / $gmv : 0;
        $commissionRate = $gmv > 0 ? $commission / $gmv : 0;
        $netGmv = $gmv - $refund;
        $netRevenue = $netGmv - $commission;

        // Phễu chuyển đổi: từ hiển thị LIVE → hiển thị sản phẩm → click → người xem → đơn hàng
        $funnel = [
            ['label' => 'Hiển thị LIVE', 'value' => $imp],
            ['label' => 'Hiển thị sản phẩm', 'value' => $productImp],
            ['label' => 'Click sản phẩm', 'value' => $productClicks],
            ['label' => 'Người xem sản phẩm', 'value' => $productViewers],
            ['label' => 'Đơn hàng', 'value' => $orders],
        ];
        $funnelMetrics = [
            'ctr' => $productImp > 0 ? $productClicks / $productImp : 0,
            'click_conv' => $productClicks > 0 ? $orders / $productClicks : 0,
        ];

        // Xếp hạng tỷ lệ hoàn theo GMV — giảm dần theo refund_rate
        $refundRanking = [];
        foreach ($creatorMap as $name => $c) {
            $refundRanking[] = [
                'creator' => $name,
                'sessions' => $c['sessions'],
                'gmv' => $c['gmv'],
                'refund' => $c['refund'],
                'refund_rate' => $c['gmv'] > 0 ? $c['refund'] / $c['gmv'] : 0,
            ];
        }
        usort($refundRanking, fn ($a, $b) => $b['refund_rate'] <=> $a['refund_rate']);

        // Xếp hạng tương tác LIVE — giảm dần theo tỷ lệ tương tác TB có trọng số
        // (tổng tương tác ÷ tổng lượt xem ước tính, mỗi phiên = tương tác ÷ tỷ lệ tương tác trong file)
        $engagementRanking = [];
        foreach ($creatorMap as $name => $c) {
            $interactions = $c['likes'] + $c['comments'] + $c['shares'];
            $engagementViews = $c['engagementViews'] ?? 0;
            $engagementRanking[] = [
                'creator' => $name,
                'sessions' => $c['sessions'],
                'likes' => $c['likes'],
                'comments' => $c['comments'],
                'shares' => $c['shares'],
                'interactions' => $interactions,
                'engagement_rate' => $engagementViews > 0 ? $interactions / $engagementViews : 0,
            ];
        }
        usort($engagementRanking, fn ($a, $b) => $b['engagement_rate'] <=> $a['engagement_rate']);

        // Hiệu suất hiển thị sản phẩm theo KOC — giảm dần theo lượt hiển thị sản phẩm
        $productMetrics = [];
        foreach ($creatorMap as $name => $c) {
            $interactions = $c['likes'] + $c['comments'] + $c['shares'];
            $productMetrics[] = [
                'creator' => $name,
                'sessions' => $c['sessions'],
                'product_imp' => $c['productImp'],
                'product_clicks' => $c['productClicks'],
                'product_viewers' => $c['productViewers'],
                'likes' => $c['likes'],
                'comments' => $c['comments'],
                'shares' => $c['shares'],
                'interactions' => $interactions,
                'ctr' => $c['productImp'] > 0 ? $c['productClicks'] / $c['productImp'] : 0,
            ];
        }
        usort($productMetrics, fn ($a, $b) => $b['product_imp'] <=> $a['product_imp']);

        return [
            'session_count' => $sessionCount,
            'gmv' => $gmv,
            'orders' => $orders,
            'items' => $items,
            'aov' => $aov,
            'commission' => $commission,
            'commission_rate' => $commissionRate,
            'net_gmv' => $netGmv,
            'net_revenue' => $netRevenue,
            'gmv_hour' => $gmvHour,
            'ref_hours' => $mins / 60,
            'refund' => $refund,
            'refund_rate' => $refundRate,
            'imp' => $imp,
            'product_imp' => $productImp,
            'product_clicks' => $productClicks,
            'product_viewers' => $productViewers,
            'likes' => $likes,
            'comments' => $comments,
            'shares' => $shares,
            'interactions' => $likes + $comments + $shares,

            // Breakdown cho các phần phân tích bên dưới
            'top_creators' => $topCreators,
            'top_products' => $topProducts,
            'hour_stats' => $hourStats,
            'daily_stats' => $dailyStats,
            'funnel' => $funnel,
            'funnel_metrics' => $funnelMetrics,
            'refund_ranking' => $refundRanking,
            'engagement_ranking' => $engagementRanking,
            'product_metrics' => $productMetrics,
            'session_details' => $details,
            'creator_filter_options' => array_map(fn ($c) => $c['creator'], $topCreators),
        ];
    }

    private static function formatDayLabel($day): string
    {
        try {
            $d = new \DateTime($day);
            return $d->format('d/m');
        } catch (\Exception $e) {
            return $day;
        }
    }

    /**
     * Chuyển chuỗi tiền VND (ví dụ "1.234.567₫") hoặc số chuẩn hoá về số thực.
     * Sửa quy tắc dấu thập phân/chuỗi hàng nghìn theo format của file TikTok.
     */
    private static function parseLocaleNumber($value): float
    {
        if ($value === null) return 0;
        if (is_int($value) || is_float($value)) return (float) $value;
        if (is_bool($value)) return 0;

        $s = (string) $value;
        $s = preg_replace('/\s+/', '', $s);
        $s = preg_replace('/[^0-9,.-]/', '', $s);
        if ($s === '' || $s === '-' || $s === '.' || $s === ',') {
            return 0;
        }

        $hasDot = strpos($s, '.') !== false;
        $hasComma = strpos($s, ',') !== false;

        if ($hasDot && $hasComma) {
            // Ký tự xuất hiện sau cùng là dấu thập phân; ký tự còn lại là chuỗi hàng nghìn.
            if (strrpos($s, ',') > strrpos($s, '.')) {
                $s = str_replace('.', '', $s);
                $s = str_replace(',', '.', $s);
            } else {
                $s = str_replace(',', '', $s);
            }
        } elseif ($hasDot) {
            $parts = explode('.', $s);
            if (count($parts) > 2 || strlen((string) end($parts)) === 3) {
                $s = str_replace('.', '', $s);
            }
        } elseif ($hasComma) {
            $parts = explode(',', $s);
            if (count($parts) > 2 || strlen((string) end($parts)) === 3) {
                $s = str_replace(',', '', $s);
            } else {
                $s = str_replace(',', '.', $s);
            }
        }

        return (float) $s;
    }

    /**
     * Phân tích giá trị tiền tệ.
     */
    private static function parseMoney($value): float
    {
        return self::parseLocaleNumber($value);
    }

    /**
     * Phân tích giá trị số đếm (số nguyên không âm).
     */
    private static function parseNumber($value): float
    {
        $n = self::parseLocaleNumber($value);
        return $n;
    }

    /**
     * Parse raw date giá trị từ Excel về DateTime object.
     * Hỗ trợ định dạng "MM/dd/yyyy H:i" của file Transaction Analysis Live List
     * (vd: "08/03/2026 21:49" = 3/8/2026 — theo mẫu file, token đầu là tháng, token giữa là ngày).
     */
    private static function parseDateRaw($raw): ?\DateTimeInterface
    {
        if ($raw instanceof \DateTimeInterface) {
            return $raw;
        }
        $str = trim((string) $raw);
        if ($str === '') {
            return null;
        }
        if (preg_match('#^(\d{1,2})/(\d{1,2})/(\d{4})\s+(\d{1,2}):(\d{2})#', $str, $m)) {
            try {
                // m[1] = tháng, m[2] = ngày, m[3] = năm
                return new \DateTime(sprintf('%04d-%02d-%02d %02d:%02d:00', $m[3], $m[1], $m[2], $m[4], $m[5]));
            } catch (\Exception $e) {
                return null;
            }
        }
        $ts = strtotime($str);
        if ($ts !== false) {
            return (new \DateTime())->setTimestamp($ts);
        }
        return null;
    }

    public function deleteUpload($filePath)
    {
        if ($filePath && Storage::disk('local')->exists($filePath)) {
            Storage::disk('local')->delete($filePath);
        }
    }
}