<?php

declare(strict_types=1);

namespace App\Controllers;

use App\Core\Auth;
use App\Core\Controller;
use App\Core\Database;
use App\Core\Request;
use App\Core\Tenant;
use App\Models\Report;

class DashboardController extends Controller
{
    public function index(Request $request): void
    {
        $companyId = Tenant::companyId();
        $canViewFinancials = $this->canViewFinancialDashboard();

        if ($companyId === null && !Auth::isSuperadmin()) {
            redirect('empresas/seleccionar');
        }

        $dashboard = $this->defaultDashboardData();
        $dashboard['canViewFinancials'] = $canViewFinancials;

        if ($companyId !== null) {
            $report = new Report();
            $dashboard['filters'] = $this->readFilters($request);
            $dashboard['branches'] = $report->branches((int) $companyId);

            $filters = [
                'fecha_desde' => $dashboard['filters']['fecha_desde'],
                'fecha_hasta' => $dashboard['filters']['fecha_hasta'],
                'usuario_id' => 0,
                'sucursal_id' => $dashboard['filters']['sucursal_id'],
            ];

            $dashboard['summary'] = $report->salesSummary((int) $companyId, $filters);
            $dashboard['topProducts'] = $report->topProducts((int) $companyId, $filters, 8);
            $dashboard['topProduct'] = $dashboard['topProducts'][0] ?? null;
            $dashboard['paymentMethods'] = array_slice($report->salesByPaymentMethod((int) $companyId, $filters), 0, 6);
            $dashboard['profitByBranch'] = array_slice($report->profitByBranch((int) $companyId, $filters), 0, 5);

            $dashboard['trend'] = $this->fetchTrendByDay((int) $companyId, $filters);
            $dashboard['trendMaxSales'] = (float) max(array_map(
                static fn(array $row): float => (float) ($row['ventas_total'] ?? 0),
                $dashboard['trend'] ?: [['ventas_total' => 0]]
            ));
            $dashboard['trendMaxProfit'] = (float) max(array_map(
                static fn(array $row): float => max(0.0, (float) ($row['utilidad_total'] ?? 0)),
                $dashboard['trend'] ?: [['utilidad_total' => 0]]
            ));

            $comparisonFilters = $this->comparisonFilters($dashboard['filters']);
            $previousSummary = $report->salesSummary((int) $companyId, [
                'fecha_desde' => $comparisonFilters['fecha_desde'],
                'fecha_hasta' => $comparisonFilters['fecha_hasta'],
                'usuario_id' => 0,
                'sucursal_id' => $dashboard['filters']['sucursal_id'],
            ]);

            $dashboard['variations'] = [
                'ventas_total' => $this->variation((float) ($dashboard['summary']['ventas_total'] ?? 0), (float) ($previousSummary['ventas_total'] ?? 0)),
                'utilidad_estimada' => $this->variation((float) ($dashboard['summary']['utilidad_estimada'] ?? 0), (float) ($previousSummary['utilidad_estimada'] ?? 0)),
                'ticket_promedio' => $this->variation((float) ($dashboard['summary']['ticket_promedio'] ?? 0), (float) ($previousSummary['ticket_promedio'] ?? 0)),
                'ventas_count' => $this->variation((float) ($dashboard['summary']['ventas_count'] ?? 0), (float) ($previousSummary['ventas_count'] ?? 0)),
                'utilidad_por_venta' => $this->variation(
                    ((float) ($dashboard['summary']['ventas_count'] ?? 0)) > 0
                        ? ((float) ($dashboard['summary']['utilidad_estimada'] ?? 0) / (float) ($dashboard['summary']['ventas_count'] ?? 0))
                        : 0.0,
                    ((float) ($previousSummary['ventas_count'] ?? 0)) > 0
                        ? ((float) ($previousSummary['utilidad_estimada'] ?? 0) / (float) ($previousSummary['ventas_count'] ?? 0))
                        : 0.0
                ),
            ];

            $pdo = Database::connection();

            $stmt = $pdo->prepare('SELECT COALESCE(SUM(total), 0) FROM ventas WHERE empresa_id = :empresa_id AND DATE(fecha) = CURDATE() AND estado <> "anulada"');
            $stmt->execute([':empresa_id' => $companyId]);
            $dashboard['today']['ventas_total'] = (float) $stmt->fetchColumn();

            $stmt = $pdo->prepare('SELECT COUNT(*) FROM clientes WHERE empresa_id = :empresa_id');
            $stmt->execute([':empresa_id' => $companyId]);
            $dashboard['today']['clientes_total'] = (int) $stmt->fetchColumn();

            $stmt = $pdo->prepare('SELECT COUNT(*) FROM productos WHERE empresa_id = :empresa_id');
            $stmt->execute([':empresa_id' => $companyId]);
            $dashboard['today']['productos_total'] = (int) $stmt->fetchColumn();

            $stmt = $pdo->prepare('SELECT COUNT(*) FROM productos WHERE empresa_id = :empresa_id AND stock_actual <= stock_minimo');
            $stmt->execute([':empresa_id' => $companyId]);
            $dashboard['today']['stock_bajo'] = (int) $stmt->fetchColumn();
        }

        $this->render('dashboard/index', [
            'title' => 'Dashboard',
            'dashboard' => $dashboard,
            'user' => Auth::user(),
        ]);
    }

    private function readFilters(Request $request): array
    {
        $today = date('Y-m-d');
        $period = strtolower(trim((string) $request->query('periodo', '30d')));
        $allowedPeriods = ['hoy', '7d', '30d', '90d', 'personalizado'];
        if (!in_array($period, $allowedPeriods, true)) {
            $period = '30d';
        }

        $dateFrom = trim((string) $request->query('fecha_desde', ''));
        $dateTo = trim((string) $request->query('fecha_hasta', ''));
        $branchId = max(0, (int) $request->query('sucursal_id', 0));

        if ($period === 'hoy') {
            $dateFrom = $today;
            $dateTo = $today;
        } elseif ($period === '7d') {
            $dateFrom = date('Y-m-d', strtotime('-6 days'));
            $dateTo = $today;
        } elseif ($period === '30d') {
            $dateFrom = date('Y-m-d', strtotime('-29 days'));
            $dateTo = $today;
        } elseif ($period === '90d') {
            $dateFrom = date('Y-m-d', strtotime('-89 days'));
            $dateTo = $today;
        } else {
            if (!$this->isDate($dateFrom) || !$this->isDate($dateTo)) {
                $dateFrom = date('Y-m-d', strtotime('-29 days'));
                $dateTo = $today;
                $period = '30d';
            }
        }

        if ($dateFrom > $dateTo) {
            [$dateFrom, $dateTo] = [$dateTo, $dateFrom];
        }

        return [
            'periodo' => $period,
            'fecha_desde' => $dateFrom,
            'fecha_hasta' => $dateTo,
            'sucursal_id' => $branchId,
        ];
    }

    private function fetchTrendByDay(int $companyId, array $filters): array
    {
        $trendTo = (string) $filters['fecha_hasta'];
        $trendFrom = (string) $filters['fecha_desde'];

        $windowStart = date('Y-m-d', strtotime($trendTo . ' -13 days'));
        if ($windowStart > $trendFrom) {
            $trendFrom = $windowStart;
        }

        $params = [
            ':empresa_id' => $companyId,
            ':fecha_desde' => $trendFrom,
            ':fecha_hasta' => $trendTo,
        ];

        $branchWhere = '';
        $branchId = (int) ($filters['sucursal_id'] ?? 0);
        if ($branchId > 0) {
            $branchWhere = ' AND (v.sucursal_id = :sucursal_id OR (v.sucursal_id IS NULL AND cj.sucursal_id = :sucursal_id_caja))';
            $params[':sucursal_id'] = $branchId;
            $params[':sucursal_id_caja'] = $branchId;
        }

        $sql = 'SELECT
                    DATE(v.fecha) AS fecha,
                    COALESCE(SUM(v.total), 0) AS ventas_total,
                    COALESCE(SUM(v.total - COALESCE(costos.total_costo, 0)), 0) AS utilidad_total
                FROM ventas v
                LEFT JOIN (
                    SELECT vd.venta_id, vd.empresa_id, SUM(vd.cantidad * p.precio_compra) AS total_costo
                    FROM venta_detalles vd
                    INNER JOIN productos p ON p.id = vd.producto_id AND p.empresa_id = vd.empresa_id
                    GROUP BY vd.venta_id, vd.empresa_id
                ) costos ON costos.venta_id = v.id AND costos.empresa_id = v.empresa_id
                LEFT JOIN aperturas_caja ac ON ac.id = v.apertura_caja_id
                LEFT JOIN cajas cj ON cj.id = ac.caja_id
                WHERE v.empresa_id = :empresa_id
                  AND v.estado <> "anulada"
                  AND DATE(v.fecha) BETWEEN :fecha_desde AND :fecha_hasta'
                . $branchWhere . '
                GROUP BY DATE(v.fecha)
                ORDER BY DATE(v.fecha) ASC';

        $stmt = Database::connection()->prepare($sql);
        $stmt->execute($params);
        $rows = $stmt->fetchAll() ?: [];

        $indexed = [];
        foreach ($rows as $row) {
            $date = (string) ($row['fecha'] ?? '');
            if ($date === '') {
                continue;
            }
            $indexed[$date] = [
                'ventas_total' => (float) ($row['ventas_total'] ?? 0),
                'utilidad_total' => (float) ($row['utilidad_total'] ?? 0),
            ];
        }

        $series = [];
        $cursor = strtotime($trendFrom);
        $end = strtotime($trendTo);
        while ($cursor !== false && $end !== false && $cursor <= $end) {
            $date = date('Y-m-d', $cursor);
            $row = $indexed[$date] ?? ['ventas_total' => 0.0, 'utilidad_total' => 0.0];
            $series[] = [
                'fecha' => $date,
                'label' => $this->shortDateLabel($date),
                'ventas_total' => (float) $row['ventas_total'],
                'utilidad_total' => (float) $row['utilidad_total'],
            ];
            $cursor = strtotime('+1 day', $cursor);
        }

        return $series;
    }

    private function shortDateLabel(string $date): string
    {
        $months = [
            '01' => 'ene',
            '02' => 'feb',
            '03' => 'mar',
            '04' => 'abr',
            '05' => 'may',
            '06' => 'jun',
            '07' => 'jul',
            '08' => 'ago',
            '09' => 'sep',
            '10' => 'oct',
            '11' => 'nov',
            '12' => 'dic',
        ];

        $timestamp = strtotime($date);
        if ($timestamp === false) {
            return $date;
        }

        $day = date('d', $timestamp);
        $month = $months[date('m', $timestamp)] ?? date('m', $timestamp);
        return $day . ' ' . $month;
    }

    private function comparisonFilters(array $filters): array
    {
        $from = strtotime((string) ($filters['fecha_desde'] ?? date('Y-m-d')));
        $to = strtotime((string) ($filters['fecha_hasta'] ?? date('Y-m-d')));

        if ($from === false || $to === false || $from > $to) {
            $today = date('Y-m-d');
            return [
                'fecha_desde' => date('Y-m-d', strtotime('-29 days')),
                'fecha_hasta' => $today,
            ];
        }

        $days = (int) floor(($to - $from) / 86400) + 1;
        $prevTo = strtotime('-1 day', $from);
        $prevFrom = strtotime('-' . max(1, $days) . ' days', $from);

        return [
            'fecha_desde' => date('Y-m-d', $prevFrom === false ? $from : $prevFrom),
            'fecha_hasta' => date('Y-m-d', $prevTo === false ? $to : $prevTo),
        ];
    }

    private function variation(float $current, float $previous): ?float
    {
        if (abs($previous) < 0.0001) {
            if (abs($current) < 0.0001) {
                return 0.0;
            }
            return null;
        }

        return (($current - $previous) / abs($previous)) * 100;
    }

    private function isDate(string $value): bool
    {
        return (bool) preg_match('/^\d{4}-\d{2}-\d{2}$/', $value);
    }

    private function defaultDashboardData(): array
    {
        $today = date('Y-m-d');
        return [
            'periodOptions' => [
                'hoy' => 'Hoy',
                '7d' => 'Ultimos 7 dias',
                '30d' => 'Ultimos 30 dias',
                '90d' => 'Ultimos 90 dias',
                'personalizado' => 'Personalizado',
            ],
            'filters' => [
                'periodo' => '30d',
                'fecha_desde' => date('Y-m-d', strtotime('-29 days')),
                'fecha_hasta' => $today,
                'sucursal_id' => 0,
            ],
            'branches' => [],
            'summary' => [
                'ventas_total' => 0,
                'ventas_count' => 0,
                'ticket_promedio' => 0,
                'utilidad_estimada' => 0,
            ],
            'topProducts' => [],
            'topProduct' => null,
            'paymentMethods' => [],
            'profitByBranch' => [],
            'trend' => [],
            'trendMaxSales' => 0.0,
            'trendMaxProfit' => 0.0,
            'variations' => [
                'ventas_total' => 0.0,
                'utilidad_estimada' => 0.0,
                'ticket_promedio' => 0.0,
                'ventas_count' => 0.0,
                'utilidad_por_venta' => 0.0,
            ],
            'today' => [
                'ventas_total' => 0.0,
                'clientes_total' => 0,
                'productos_total' => 0,
                'stock_bajo' => 0,
            ],
            'canViewFinancials' => false,
        ];
    }

    private function canViewFinancialDashboard(): bool
    {
        if (Auth::isSuperadmin()) {
            return true;
        }

        $user = Auth::user();
        if (!is_array($user)) {
            return false;
        }

        return strtolower(trim((string) ($user['role_slug'] ?? ''))) === 'admin_empresa';
    }
}
