<?php

namespace App\Console\Commands;

use App\Facades\Settings;
use App\Models\TenantDailyMetric;
use App\Services\CurrencyService;
use App\Services\GraphService;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;
use Mtc\MercuryDataModels\Booking;
use Mtc\MercuryDataModels\Enquiry;
use Mtc\MercuryDataModels\Tenant;
use Mtc\MercuryDataModels\Vehicle;
use Mtc\MercuryDataModels\VehicleValuation;
use Mtc\VehicleReservations\Reservation;

class CollectTenantDailyMetrics extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'metrics:collect-daily
                            {--date= : Date to collect metrics for (YYYY-MM-DD), defaults to yesterday}
                            {--from= : Start date for range collection (YYYY-MM-DD)}
                            {--to= : End date for range collection (YYYY-MM-DD), defaults to yesterday}
                            {--days= : Number of days to backfill from yesterday}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Collect daily metrics from all tenants and store in central database';
    public function __construct(private readonly GraphService $graphService)
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     */
    public function handle(): int
    {
        $dates = $this->getDatesToProcess();

        if (empty($dates)) {
            $this->error('No valid dates to process.');
            return Command::FAILURE;
        }

        $this->info("Collecting metrics for " . count($dates) . " day(s)");

        $tenants = Tenant::query()
            ->whereNull('suspended_at')
            ->get();

        $this->info("Found {$tenants->count()} active tenants");

        $totalOperations = $tenants->count() * count($dates);
        $bar = $this->output->createProgressBar($totalOperations);
        $bar->start();

        foreach ($dates as $date) {
            foreach ($tenants as $tenant) {
                $this->collectMetricsForTenant($tenant, $date);
                $bar->advance();
            }
        }

        $bar->finish();
        $this->newLine();
        $this->info('Metrics collection complete!');

        return Command::SUCCESS;
    }

    /**
     * Get the dates to process based on command options.
     */
    protected function getDatesToProcess(): array
    {
        // Single date option
        if ($this->option('date')) {
            return [Carbon::parse($this->option('date'))];
        }

        // Days option - backfill X days from yesterday
        if ($this->option('days')) {
            $days = (int) $this->option('days');
            $dates = [];
            for ($i = $days; $i >= 1; $i--) {
                $dates[] = Carbon::yesterday()->subDays($i - 1);
            }
            return $dates;
        }

        // Date range option
        if ($this->option('from')) {
            $from = Carbon::parse($this->option('from'));
            $to = $this->option('to') ? Carbon::parse($this->option('to')) : Carbon::yesterday();

            $dates = [];
            $current = $from->copy();
            while ($current->lte($to)) {
                $dates[] = $current->copy();
                $current->addDay();
            }
            return $dates;
        }

        // Default: yesterday only
        return [Carbon::yesterday()];
    }

    /**
     * Collect metrics for a single tenant.
     */
    protected function collectMetricsForTenant(Tenant $tenant, Carbon $date): void
    {
        tenancy()->initialize($tenant);

        try {
            $startOfDay = $date->copy()->startOfDay();
            $endOfDay = $date->copy()->endOfDay();

            $enquiriesCount = $this->countEnquiries($startOfDay, $endOfDay);
            $serviceBookingsCount = $this->countServiceBookings($startOfDay, $endOfDay);
            $reservationsCount = $this->countReservations($startOfDay, $endOfDay);
            $visitors = $this->fetchVisitorCount($tenant, $date);
            $revenue = $this->calculateRevenue($startOfDay, $endOfDay);

            $metrics = [
                'enquiries_count' => $enquiriesCount,
                'service_bookings_count' => $serviceBookingsCount,
                'reservations_count' => $reservationsCount,
                'valuations_count' => $this->countValuations($startOfDay, $endOfDay),
                'vehicles_added' => $this->countVehiclesAdded($startOfDay, $endOfDay),
                'vehicles_exited' => $this->countVehiclesExited($startOfDay, $endOfDay),
                'visitors' => $visitors,
                'enquiry_conversion_rate' => $this->calculateConversionRate($enquiriesCount, $visitors),
                'booking_conversion_rate' => $this->calculateConversionRate($serviceBookingsCount, $visitors),
                'reservation_conversion_rate' => $this->calculateConversionRate($reservationsCount, $visitors),
                'revenue' => $revenue,
            ];

            TenantDailyMetric::query()->updateOrCreate(
                [
                    'tenant_id' => $tenant->id,
                    'date' => $date->format('Y-m-d'),
                ],
                $metrics
            );
        } catch (\Exception $e) {
            $this->error("Error collecting metrics for tenant {$tenant->id}: {$e->getMessage()}");
        } finally {
            tenancy()->end();
        }
    }

    /**
     * Count enquiries for the given date range.
     */
    protected function countEnquiries(Carbon $startOfDay, Carbon $endOfDay): int
    {
        return Enquiry::query()
            ->whereNotNull('ingested_at')
            ->whereBetween('created_at', [$startOfDay, $endOfDay])
            ->count();
    }

    /**
     * Count service bookings for the given date range.
     */
    protected function countServiceBookings(Carbon $startOfDay, Carbon $endOfDay): int
    {
        return Booking::query()
            ->whereNotNull('ingested_at')
            ->whereBetween('created_at', [$startOfDay, $endOfDay])
            ->count();
    }

    /**
     * Count reservations for the given date range.
     */
    protected function countReservations(Carbon $startOfDay, Carbon $endOfDay): int
    {
        return Reservation::query()
            ->whereNotNull('ingested_at')
            ->whereBetween('created_at', [$startOfDay, $endOfDay])
            ->count();
    }

    /**
     * Count valuations for the given date range.
     */
    protected function countValuations(Carbon $startOfDay, Carbon $endOfDay): int
    {
        return VehicleValuation::query()
            ->whereBetween('created_at', [$startOfDay, $endOfDay])
            ->count();
    }

    /**
     * Count vehicles added (created) for the given date range.
     */
    protected function countVehiclesAdded(Carbon $startOfDay, Carbon $endOfDay): int
    {
        return Vehicle::query()
            ->whereBetween('created_at', [$startOfDay, $endOfDay])
            ->count();
    }

    /**
     * Count vehicles exited (deleted or sold) for the given date range.
     */
    protected function countVehiclesExited(Carbon $startOfDay, Carbon $endOfDay): int
    {
        $deletedCount = Vehicle::query()
            ->withTrashed()
            ->whereNotNull('deleted_at')
            ->whereBetween('deleted_at', [$startOfDay, $endOfDay])
            ->count();

        $soldCount = Vehicle::query()
            ->where('is_sold', true)
            ->whereBetween('sold_at', [$startOfDay, $endOfDay])
            ->count();

        return $deletedCount + $soldCount;
    }

    protected function fetchVisitorCount(Tenant $tenant, Carbon $date): int
    {
        return $this->graphService->visitorCount($tenant, $date);
    }

    /**
     * Calculate conversion rate as a percentage.
     */
    protected function calculateConversionRate(int $conversions, int $visitors): float
    {
        if ($visitors === 0) {
            return 0.00;
        }

        return round(($conversions / $visitors) * 100, 2);
    }

    /**
     * Calculate revenue from confirmed reservations for the given date range.
     * Converts to GBP as base currency.
     */
    protected function calculateRevenue(Carbon $startOfDay, Carbon $endOfDay): float
    {
        $revenue = (float) Reservation::query()
            ->whereNotNull('confirmed_at')
            ->whereBetween('confirmed_at', [$startOfDay, $endOfDay])
            ->sum('amount');

        if ($revenue === 0.0) {
            return 0.0;
        }

        $tenantCurrency = Settings::get('app-details-currency', 'GBP');

        if ($tenantCurrency === 'GBP') {
            return $revenue;
        }

        try {
            $currencyService = app(CurrencyService::class);
            return $currencyService->convertToCurrency($revenue, $tenantCurrency, 'GBP');
        } catch (\Exception $e) {
            Log::warning("Failed to convert revenue to GBP: {$e->getMessage()}");
            return $revenue;
        }
    }
}
