<?php
namespace Analytics\Services;

use Analytics\Repositories\EventDataRepository;
use Analytics\Repositories\PageDataRepository;
use Analytics\Repositories\QueryDataRepository;
use Analytics\Repositories\SnapshotRepository;

/**
 * Assembles report data for different audiences (Business Owner / Client / Team).
 * Also computes ROAS and Campaign Health Score.
 */
class ReportBuilderService
{
    private SnapshotRepository  $snapshots;
    private EventDataRepository $events;
    private PageDataRepository  $pages;
    private QueryDataRepository $queries;

    // Path to PPC/config.json for global CPL values
    private string $ppcConfigPath;

    public function __construct()
    {
        $this->snapshots     = new SnapshotRepository();
        $this->events        = new EventDataRepository();
        $this->pages         = new PageDataRepository();
        $this->queries       = new QueryDataRepository();
        $this->ppcConfigPath = dirname(__DIR__, 2) . '/PPC/config.json';
    }

    // ── CPL Resolution ────────────────────────────────────────────────────────

    public function getGlobalCpls(): array
    {
        if (!file_exists($this->ppcConfigPath)) return [];
        $config = json_decode(file_get_contents($this->ppcConfigPath), true) ?? [];
        return $config['lead_category_cpls'] ?? [];
    }

    public function effectiveCpl(string $categoryKey, ?float $override): float
    {
        if ($override !== null) return $override;
        $globals = $this->getGlobalCpls();
        return (float)($globals[$categoryKey] ?? 0);
    }

    // ── ROAS Calculation ──────────────────────────────────────────────────────

    public function calcRoas(array $snapshot, array $project): float
    {
        $revenueManaged = (float)($project['revenue_managed'] ?? 0);
        if ($revenueManaged <= 0) return 0;

        $leadValue = $this->calcLeadValue($snapshot, $project);
        return round($leadValue / $revenueManaged, 2);
    }

    public function calcLeadValue(array $snapshot, array $project): float
    {
        $categories = $project['lead_categories'] ?? [];
        $totals     = $snapshot['lead_category_totals'] ?? [];
        $value      = 0;

        foreach ($categories as $cat) {
            $key      = $cat['key'] ?? '';
            $override = isset($cat['cpl_override']) && $cat['cpl_override'] !== null ? (float)$cat['cpl_override'] : null;
            $cpl      = $this->effectiveCpl($key, $override);
            $count    = (int)($totals[$key] ?? 0);
            $value   += $count * $cpl;
        }

        // Add e-commerce revenue
        $value += (float)($snapshot['organic_revenue'] ?? 0);

        return $value;
    }

    // ── Health Score (0–100) ──────────────────────────────────────────────────

    public function calcHealthScore(array $current, ?array $previous): int
    {
        if (!$previous) return 50; // neutral if no comparison

        $score = 0;

        // Sessions trend: 25 pts
        $score += $this->trendPoints($current['organic_sessions'] ?? 0, $previous['organic_sessions'] ?? 0, 25);

        // Leads trend: 30 pts
        $score += $this->trendPoints($current['total_leads'] ?? 0, $previous['total_leads'] ?? 0, 30);

        // GSC Position improvement (lower = better): 20 pts
        $curPos  = (float)($current['gsc_position']  ?? 0);
        $prevPos = (float)($previous['gsc_position'] ?? 0);
        if ($prevPos > 0 && $curPos > 0) {
            $score += $curPos < $prevPos ? 20 : ($curPos === $prevPos ? 10 : 0);
        } else {
            $score += 10;
        }

        // Branded impressions trend: 15 pts
        $score += $this->trendPoints($current['branded_impressions'] ?? 0, $previous['branded_impressions'] ?? 0, 15);

        // AI/LLM traffic growth: 10 pts
        $score += $this->trendPoints($current['llm_total'] ?? 0, $previous['llm_total'] ?? 0, 10);

        return min(100, max(0, $score));
    }

    private function trendPoints(int $curr, int $prev, int $maxPts): int
    {
        if ($prev <= 0) return (int)($maxPts * 0.6); // neutral if no data
        $delta = ($curr - $prev) / $prev;
        if ($delta >= 0.1)  return $maxPts;
        if ($delta >= 0.0)  return (int)($maxPts * 0.8);
        if ($delta >= -0.1) return (int)($maxPts * 0.5);
        if ($delta >= -0.2) return (int)($maxPts * 0.2);
        return 0;
    }

    // ── Delta % Calculation ───────────────────────────────────────────────────

    public function delta(float $curr, float $prev): ?float
    {
        if ($prev <= 0) return null;
        return round(($curr - $prev) / $prev * 100, 1);
    }

    public function deltaObj(float $curr, float $prev): ?array
    {
        if ($prev <= 0) return null;
        return [
            'abs' => $curr - $prev,
            'pct' => round(($curr - $prev) / $prev * 100, 1)
        ];
    }

    // ── Audience Report Assembly ──────────────────────────────────────────────

    public function buildReport(int $projectId, array $project, string $start, string $end, string $audience = 'client', string $compareMode = 'period'): array
    {
        if ($audience === 'business_owner') {
            $audience = 'business';
        }

        $snapshot = $this->snapshots->find($projectId, $start, $end);
        if (!$snapshot) {
            return ['error' => 'No data for this period. Please run the tracker first.'];
        }

        $previous   = $this->snapshots->previousByMode($projectId, $start, $end, $compareMode);
        $cmpRange   = $this->snapshots->comparisonRange($start, $end, $compareMode);
        $healthScore= $this->calcHealthScore($snapshot, $previous);
        $roas       = $this->calcRoas($snapshot, $project);
        $leadValue  = $this->calcLeadValue($snapshot, $project);

        $base = [
            'project'              => $project,
            'snapshot'             => $snapshot,
            'previous'             => $previous,
            'period'               => ['start' => $start, 'end' => $end],
            'compare_mode'         => $compareMode,
            'compare_range'        => $cmpRange,
            'health_score'         => $healthScore,
            'roas'                 => $roas,
            'lead_value'           => $leadValue,
            'categories'           => $this->enrichCategories($snapshot, $project),
        ];

        return match ($audience) {
            'business' => $this->businessOwnerReport($base),
            'team'     => $this->teamReport($base),
            default    => $this->clientReport($base),
        };
    }

    private function enrichCategories(array $snapshot, array $project): array
    {
        $categories  = $project['lead_categories']       ?? [];
        $totals      = $snapshot['lead_category_totals'] ?? [];
        $globals     = $this->getGlobalCpls();

        return array_map(function (array $cat) use ($totals, $globals): array {
            $key      = $cat['key']       ?? '';
            $override = $cat['cpl_override'] ?? null;
            $cpl      = $override !== null ? (float)$override : (float)($globals[$key] ?? 0);
            $count    = (int)($totals[$key] ?? 0);
            return array_merge($cat, [
                'total'       => $count,
                'effective_cpl' => $cpl,
                'value'       => $count * $cpl,
                'using_override' => $override !== null,
            ]);
        }, $categories);
    }

    private function businessOwnerReport(array $base): array
    {
        $s = $base['snapshot'];
        $p = $base['previous'];
        return array_merge($base, [
            'audience'       => 'business',
            'audience_label' => 'Business Owner',
            'kpis'           => [
                ['label' => 'Revenue Managed',   'value' => $base['project']['revenue_managed'] ?? 0, 'format' => 'currency'],
                ['label' => 'Estimated Lead Value','value' => $base['lead_value'], 'format' => 'currency', 'delta' => null],
                ['label' => 'ROAS',              'value' => $base['roas'],         'format' => 'multiplier'],
                ['label' => 'Total Leads',       'value' => $s['total_leads'] ?? 0, 'format' => 'number', 'delta' => $p ? $this->delta($s['total_leads'] ?? 0, $p['total_leads'] ?? 0) : null],
                ['label' => 'Organic Orders',    'value' => $s['organic_orders'] ?? 0, 'format' => 'number'],
                ['label' => 'Organic Revenue',   'value' => $s['organic_revenue'] ?? 0, 'format' => 'currency'],
                ['label' => 'Overall Users',     'value' => $s['overall_users'] ?? 0, 'format' => 'number', 'delta' => $p ? $this->delta($s['overall_users'] ?? 0, $p['overall_users'] ?? 0) : null],
                ['label' => 'LLM Traffic',       'value' => $s['llm_total'] ?? 0,  'format' => 'number', 'delta' => $p ? $this->delta($s['llm_total'] ?? 0, $p['llm_total'] ?? 0) : null],
                ['label' => 'AI Overview',       'value' => $s['ai_overview'] ?? 0,'format' => 'number'],
                ['label' => 'Health Score',      'value' => $base['health_score'],  'format' => 'score'],
                ['label' => 'Client Age (mo.)',  'value' => $this->clientAge($base['project']['onboard_date'] ?? null), 'format' => 'number'],
            ],
        ]);
    }

    private function clientReport(array $base): array
    {
        $s = $base['snapshot'];
        $p = $base['previous'];
        return array_merge($base, [
            'audience'       => 'client',
            'audience_label' => 'Client',
            'kpis'           => [
                ['label' => 'Organic Sessions',  'value' => $s['organic_sessions']  ?? 0, 'format' => 'number', 'delta' => $p ? $this->delta($s['organic_sessions'] ?? 0, $p['organic_sessions'] ?? 0) : null],
                ['label' => 'GSC Clicks',        'value' => $s['gsc_clicks']        ?? 0, 'format' => 'number', 'delta' => $p ? $this->delta($s['gsc_clicks'] ?? 0, $p['gsc_clicks'] ?? 0) : null],
                ['label' => 'Impressions',       'value' => $s['gsc_impressions']   ?? 0, 'format' => 'number', 'delta' => $p ? $this->delta($s['gsc_impressions'] ?? 0, $p['gsc_impressions'] ?? 0) : null],
                ['label' => 'Avg Position',      'value' => $s['gsc_position']      ?? 0, 'format' => 'decimal', 'delta' => null],
                ['label' => 'CTR',               'value' => round(($s['gsc_ctr'] ?? 0) * 100, 2), 'format' => 'percent'],
                ['label' => 'Branded Clicks',    'value' => $s['branded_clicks']    ?? 0, 'format' => 'number'],
                ['label' => 'Non-Branded Clicks','value' => $s['non_branded_clicks']?? 0, 'format' => 'number'],
                ['label' => 'Total Leads',       'value' => $s['total_leads']       ?? 0, 'format' => 'number', 'delta' => $p ? $this->delta($s['total_leads'] ?? 0, $p['total_leads'] ?? 0) : null],
                ['label' => 'Engagement Rate',   'value' => round(($s['organic_engagement_rate'] ?? 0) * 100, 1), 'format' => 'percent'],
            ],
        ]);
    }

    private function teamReport(array $base): array
    {
        $report = $this->clientReport($base);
        $s      = $base['snapshot'];
        $sid    = (int)($s['id'] ?? 0);

        // Extend with team-level data
        $report['audience']       = 'team';
        $report['audience_label'] = 'Team';
        $report['event_drill']    = $sid ? $this->events->bySnapshot($sid) : [];
        $report['page_data']      = $sid ? $this->pages->bySnapshot($sid, 200) : [];
        $report['query_data']     = $sid ? $this->queries->bySnapshot($sid, 100) : [];

        // Additional team KPIs
        $report['kpis'] = array_merge($report['kpis'], [
            ['label' => 'Paid Sessions',     'value' => $s['paid_sessions']     ?? 0, 'format' => 'number'],
            ['label' => 'Direct Sessions',   'value' => $s['direct_sessions']   ?? 0, 'format' => 'number'],
            ['label' => 'Social Sessions',   'value' => $s['social_sessions']   ?? 0, 'format' => 'number'],
            ['label' => 'Referral Sessions', 'value' => $s['referral_sessions'] ?? 0, 'format' => 'number'],
            ['label' => 'Engaged Sessions',  'value' => $s['organic_engaged_sessions'] ?? 0, 'format' => 'number'],
            ['label' => 'Avg Engagement',    'value' => round($s['organic_avg_engagement_time'] ?? 0), 'format' => 'seconds'],
            ['label' => 'Page Views',        'value' => $s['organic_page_views'] ?? 0, 'format' => 'number'],
            ['label' => 'New Users',         'value' => $s['new_users']          ?? 0, 'format' => 'number'],
        ]);

        return $report;
    }

    // ── Manager Rollup ────────────────────────────────────────────────────────

    public function buildManagerRollup(array $projects, string $start, string $end): array
    {
        $projectIds = array_column($projects, 'id');
        $snapshots  = $this->snapshots->latestPerProject(array_map('intval', $projectIds)); // For dashboard, keep latest (or could use batchFind)

        // Also get previous period snapshots for delta
        $days = (int)(new \DateTime($start))->diff(new \DateTime($end))->days;
        $prevEnd   = date('Y-m-d', strtotime($start . ' -1 day'));
        $prevStart = date('Y-m-d', strtotime($prevEnd . " -{$days} days"));
        
        // Since SnapshotRepository does not have batchFind, we fetch previous data one by one for now
        $prevSnaps = [];
        foreach ($projectIds as $pid) {
            $prevSnaps[(int)$pid] = $this->snapshots->previous((int)$pid, $start, $end);
        }

        $byManager = [];
        foreach ($projects as $p) {
            $mgr  = $p['manager_name'] ?: 'Unassigned';
            $snap = $snapshots[(int)$p['id']] ?? null;
            $prev = null;

            if (!isset($byManager[$mgr])) {
                $byManager[$mgr] = ['manager' => $mgr, 'clients' => 0, 'declining' => 0, 'projects' => [],
                    'sessions' => 0, 'prev_sessions' => 0,
                    'clicks' => 0, 'prev_clicks' => 0,
                    'impressions' => 0, 'prev_impressions' => 0,
                    'leads' => 0, 'prev_leads' => 0,
                    'llm' => 0, 'prev_llm' => 0,
                    'lead_value' => 0, 'revenue_managed' => 0,
                ];
            }

            $row = &$byManager[$mgr];
            $row['clients']++;
            $row['projects'][] = (int)$p['id'];
            $row['revenue_managed'] += (float)($p['revenue_managed'] ?? 0);

            if ($snap) {
                $row['sessions']    += (int)($snap['organic_sessions']  ?? 0);
                $row['clicks']      += (int)($snap['gsc_clicks']        ?? 0);
                $row['impressions'] += (int)($snap['gsc_impressions']   ?? 0);
                $row['leads']       += (int)($snap['total_leads']       ?? 0);
                $row['llm']         += (int)($snap['llm_total']         ?? 0);
                $row['lead_value']  += $this->calcLeadValue($snap, $p);
                
                $prev = $prevSnaps[(int)$p['id']] ?? null;
                if ($prev) {
                    $row['prev_sessions']    += (int)($prev['organic_sessions']  ?? 0);
                    $row['prev_clicks']      += (int)($prev['gsc_clicks']        ?? 0);
                    $row['prev_impressions'] += (int)($prev['gsc_impressions']   ?? 0);
                    $row['prev_leads']       += (int)($prev['total_leads']       ?? 0);
                    $row['prev_llm']         += (int)($prev['llm_total']         ?? 0);
                }

                // Declining leads indicator
                if ($prev && ($snap['total_leads'] ?? 0) < ($prev['total_leads'] ?? 0)) {
                    $row['declining']++;
                }
            }
        }

        // Compute ROAS per manager + health color
        $result = [];
        foreach ($byManager as $mgr => $row) {
            $roas = $row['revenue_managed'] > 0 ? round($row['lead_value'] / $row['revenue_managed'], 2) : 0;
            $health = $roas >= 3 ? 'green' : ($roas >= 1.5 ? 'yellow' : 'red');
            $result[] = array_merge($row, [
                'roas'         => $roas,
                'health_color' => $health,
                'delta_sessions'    => $this->deltaObj($row['sessions'],    $row['prev_sessions']),
                'delta_clicks'      => $this->deltaObj($row['clicks'],      $row['prev_clicks']),
                'delta_impressions' => $this->deltaObj($row['impressions'],  $row['prev_impressions']),
                'delta_leads'       => $this->deltaObj($row['leads'],        $row['prev_leads']),
                'delta_llm'         => $this->deltaObj($row['llm'],          $row['prev_llm']),
            ]);
        }

        usort($result, fn($a, $b) => $b['clients'] <=> $a['clients']);
        return $result;
    }

    private function clientAge(?string $onboardDate): int
    {
        if (!$onboardDate) return 0;
        try {
            $diff = (new \DateTime($onboardDate))->diff(new \DateTime());
            return $diff->y * 12 + $diff->m;
        } catch (\Throwable) { return 0; }
    }
}
