using Microsoft.AspNetCore.Mvc;
using Meezi.API.Models.Reports;
using Meezi.API.Services;
using Meezi.API.Utils;
using Meezi.Core.Enums;
using Meezi.Core.Interfaces;
using Meezi.Shared;

namespace Meezi.API.Controllers;

[Route("api/cafes/{cafeId}/reports")]
public class ReportsController : CafeApiControllerBase
{
    private readonly IReportService _reports;
    private readonly IDailyReportService _dailyReports;

    public ReportsController(IReportService reports, IDailyReportService dailyReports)
    {
        _reports = reports;
        _dailyReports = dailyReports;
    }

    [HttpGet("daily")]
    public async Task<IActionResult> GetDailySnapshot(
        string cafeId,
        [FromQuery] string branchId,
        [FromQuery] string? date,
        ITenantContext tenant,
        CancellationToken ct)
    {
        if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
        if (string.IsNullOrWhiteSpace(branchId))
            return BadRequest(new ApiResponse<object>(false, null,
                new ApiError("VALIDATION_ERROR", "branchId is required.", "branchId")));

        if (!TryParseReportDate(date, out var reportDate))
            return BadRequest(new ApiResponse<object>(false, null,
                new ApiError("VALIDATION_ERROR", "Invalid date. Use yyyy-MM-dd.", "date")));

        if (EnsureReportDateAllowed(tenant, reportDate) is { } planError) return planError;

        var snapshot = await _dailyReports.GetReportAsync(cafeId, branchId, reportDate, ct);
        if (snapshot is null)
            snapshot = await _dailyReports.GenerateReportAsync(cafeId, branchId, reportDate, ct);

        return Ok(new ApiResponse<DailyReportSnapshotDto>(true, snapshot));
    }

    [HttpGet("daily/range")]
    public async Task<IActionResult> GetDailyRange(
        string cafeId,
        [FromQuery] string? branchId,
        [FromQuery] string from,
        [FromQuery] string to,
        ITenantContext tenant,
        CancellationToken ct)
    {
        if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;

        if (!TryParseReportDate(from, out var startDate) || !TryParseReportDate(to, out var endDate))
            return BadRequest(new ApiResponse<object>(false, null,
                new ApiError("VALIDATION_ERROR", "Invalid from/to. Use yyyy-MM-dd.", "from")));

        var today = IranCalendar.TodayInIran;
        var tier = tenant.PlanTier ?? PlanTier.Free;

        if (!ReportPlanGate.IsDateInRange(tier, startDate, today)
            || !ReportPlanGate.IsDateInRange(tier, endDate, today))
        {
            return StatusCode(403, new ApiResponse<object>(false, null,
                new ApiError("PLAN_LIMIT_REACHED", ReportPlanGate.LimitMessage(tier), "date")));
        }

        var clamped = ReportPlanGate.ClampRange(tier, startDate, endDate, today);
        if (clamped is null)
            return BadRequest(new ApiResponse<object>(false, null,
                new ApiError("VALIDATION_ERROR", "Invalid date range.", "from")));

        var data = await _dailyReports.GetReportRangeAsync(
            cafeId, branchId, clamped.Value.From, clamped.Value.To, ct);

        return Ok(new ApiResponse<IReadOnlyList<DailyReportSnapshotDto>>(true, data));
    }

    [HttpGet("summary")]
    public async Task<IActionResult> GetSummary(
        string cafeId,
        ITenantContext tenant,
        [FromQuery] int days = 30,
        CancellationToken ct = default)
    {
        if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;

        var tier = tenant.PlanTier ?? PlanTier.Free;
        var maxDays = Core.Constants.PlanLimits.MaxReportHistoryDays(tier);
        if (days > maxDays && maxDays != int.MaxValue)
        {
            return StatusCode(403, new ApiResponse<object>(false, null,
                new ApiError("PLAN_LIMIT_REACHED", ReportPlanGate.LimitMessage(tier), "days")));
        }

        days = Math.Min(days, maxDays == int.MaxValue ? 365 : maxDays);
        var data = await _dailyReports.GetSummaryAsync(cafeId, days, ct);
        return Ok(new ApiResponse<DailyReportSummaryDto>(true, data));
    }

    [HttpGet("daily/live")]
    public async Task<IActionResult> GetDailyLive(
        string cafeId,
        [FromQuery] string? date,
        ITenantContext tenant,
        CancellationToken ct)
    {
        if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
        if (date is not null && !JalaliCalendarHelper.TryParseJalaliDate(date, out _, out _, out _))
            return BadRequest(new ApiResponse<object>(false, null,
                new ApiError("VALIDATION_ERROR", "Invalid Jalali date. Use yyyy-MM-dd.")));

        var data = await _reports.GetDailyReportAsync(cafeId, date ?? string.Empty, ct);
        return Ok(new ApiResponse<DailyReportDto>(true, data));
    }

    [HttpGet("monthly")]
    public async Task<IActionResult> GetMonthly(
        string cafeId,
        [FromQuery] string? month,
        ITenantContext tenant,
        CancellationToken ct)
    {
        if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
        if (month is not null && !JalaliCalendarHelper.TryParseJalaliMonth(month, out _, out _))
            return BadRequest(new ApiResponse<object>(false, null,
                new ApiError("VALIDATION_ERROR", "Invalid Jalali month. Use yyyy-MM.")));

        var data = await _reports.GetMonthlyReportAsync(cafeId, month ?? string.Empty, ct);
        return Ok(new ApiResponse<MonthlyReportDto>(true, data));
    }

    [HttpGet("trend")]
    public async Task<IActionResult> GetTrend(
        string cafeId,
        ITenantContext tenant,
        [FromQuery] int days = 7,
        CancellationToken ct = default)
    {
        if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
        var data = await _reports.GetTrendAsync(cafeId, days, ct);
        return Ok(new ApiResponse<IReadOnlyList<TrendDayDto>>(true, data));
    }

    [HttpGet("export")]
    public async Task<IActionResult> Export(
        string cafeId,
        [FromQuery] string month,
        ITenantContext tenant,
        [FromQuery] string format = "excel",
        CancellationToken ct = default)
    {
        if (EnsureCafeAccess(cafeId, tenant) is { } denied) return denied;
        if (!string.Equals(format, "excel", StringComparison.OrdinalIgnoreCase))
            return BadRequest(new ApiResponse<object>(false, null,
                new ApiError("VALIDATION_ERROR", "Only excel format is supported.")));
        if (!JalaliCalendarHelper.TryParseJalaliMonth(month, out _, out _))
            return BadRequest(new ApiResponse<object>(false, null,
                new ApiError("VALIDATION_ERROR", "Invalid Jalali month. Use yyyy-MM.")));

        var bytes = await _reports.ExportExcelAsync(cafeId, month, ct);
        var fileName = $"meezi-report-{month}.xlsx";
        return File(bytes, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", fileName);
    }

    private static bool TryParseReportDate(string? value, out DateOnly date)
    {
        if (string.IsNullOrWhiteSpace(value))
        {
            date = IranCalendar.TodayInIran;
            return true;
        }

        return DateOnly.TryParse(value, out date);
    }

    private IActionResult? EnsureReportDateAllowed(ITenantContext tenant, DateOnly date)
    {
        var tier = tenant.PlanTier ?? PlanTier.Free;
        var today = IranCalendar.TodayInIran;
        if (ReportPlanGate.IsDateInRange(tier, date, today))
            return null;

        return StatusCode(403, new ApiResponse<object>(false, null,
            new ApiError("PLAN_LIMIT_REACHED", ReportPlanGate.LimitMessage(tier), "date")));
    }
}
