using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.SignalR;
using Microsoft.EntityFrameworkCore;
using OTSSignsOrchestrator.Server.Data;
using OTSSignsOrchestrator.Server.Data.Entities;
using OTSSignsOrchestrator.Server.Hubs;
using Stripe.Checkout;

namespace OTSSignsOrchestrator.Server.Api;

public static class SignupApi
{
    public static void MapSignupEndpoints(this WebApplication app)
    {
        app.MapPost("/api/signup/initiate", HandleInitiate)
            .RequireRateLimiting("signup");

        app.MapGet("/api/signup/status/{token:guid}", HandleStatus);
    }

    private static async Task<IResult> HandleInitiate(
        SignupRequest req,
        OrchestratorDbContext db,
        IConfiguration config,
        ILogger<SignupRequest> logger)
    {
        // ── Validation ──────────────────────────────────────────────────────
        var errors = new List<string>();
        if (string.IsNullOrWhiteSpace(req.CompanyName))
            errors.Add("companyName is required.");
        if (string.IsNullOrWhiteSpace(req.AdminEmail) || !new EmailAddressAttribute().IsValid(req.AdminEmail))
            errors.Add("A valid adminEmail is required.");
        if (string.IsNullOrWhiteSpace(req.Plan) ||
            !req.Plan.Equals("Essentials", StringComparison.OrdinalIgnoreCase) &&
            !req.Plan.Equals("Pro", StringComparison.OrdinalIgnoreCase))
            errors.Add("plan must be 'Essentials' or 'Pro'.");
        if (req.ScreenCount < 1)
            errors.Add("screenCount must be at least 1.");
        if (req.Plan?.Equals("Essentials", StringComparison.OrdinalIgnoreCase) == true && req.ScreenCount > 50)
            errors.Add("Essentials plan supports a maximum of 50 screens.");
        if (string.IsNullOrWhiteSpace(req.BillingFrequency) ||
            !req.BillingFrequency.Equals("monthly", StringComparison.OrdinalIgnoreCase) &&
            !req.BillingFrequency.Equals("annual", StringComparison.OrdinalIgnoreCase))
            errors.Add("billingFrequency must be 'monthly' or 'annual'.");

        if (errors.Count > 0)
            return Results.ValidationProblem(
                errors.ToDictionary(e => e, _ => new[] { "Validation failed." }));

        // ── Create pending customer ─────────────────────────────────────────
        var plan = Enum.Parse<CustomerPlan>(req.Plan!, true);
        var customer = new Customer
        {
            Id = Guid.NewGuid(),
            CompanyName = req.CompanyName!.Trim(),
            AdminEmail = req.AdminEmail!.Trim().ToLowerInvariant(),
            AdminFirstName = req.AdminFirstName?.Trim() ?? string.Empty,
            AdminLastName = req.AdminLastName?.Trim() ?? string.Empty,
            Plan = plan,
            ScreenCount = req.ScreenCount,
            Status = CustomerStatus.PendingPayment,
            CreatedAt = DateTime.UtcNow,
        };

        db.Customers.Add(customer);
        await db.SaveChangesAsync();

        // ── Stripe Checkout Session ─────────────────────────────────────────
        var priceKey = $"Stripe:Prices:{req.Plan}:{req.BillingFrequency}".ToLowerInvariant();
        var priceId = config[priceKey];
        if (string.IsNullOrWhiteSpace(priceId))
        {
            logger.LogError("Stripe price ID not configured for key {PriceKey}", priceKey);
            return Results.Problem("Billing configuration error. Contact support.", statusCode: 500);
        }

        var sessionOptions = new SessionCreateOptions
        {
            Mode = "subscription",
            CustomerEmail = customer.AdminEmail,
            LineItems = new List<SessionLineItemOptions>
            {
                new()
                {
                    Price = priceId,
                    Quantity = req.ScreenCount,
                },
            },
            SubscriptionData = new SessionSubscriptionDataOptions
            {
                TrialPeriodDays = 14,
            },
            Metadata = new Dictionary<string, string>
            {
                ["ots_customer_id"] = customer.Id.ToString(),
                ["company_name"] = customer.CompanyName,
                ["admin_email"] = customer.AdminEmail,
                ["admin_first_name"] = customer.AdminFirstName,
                ["admin_last_name"] = customer.AdminLastName,
                ["plan"] = req.Plan!,
                ["screen_count"] = req.ScreenCount.ToString(),
                ["billing_frequency"] = req.BillingFrequency!,
            },
            SuccessUrl = config["Stripe:SuccessUrl"] ?? "https://app.ots-signs.com/signup/success?session_id={CHECKOUT_SESSION_ID}",
            CancelUrl = config["Stripe:CancelUrl"] ?? "https://app.ots-signs.com/signup/cancel",
        };

        var sessionService = new SessionService();
        var session = await sessionService.CreateAsync(sessionOptions);

        customer.StripeCheckoutSessionId = session.Id;
        await db.SaveChangesAsync();

        logger.LogInformation(
            "Signup initiated: customer={CustomerId}, company={Company}, plan={Plan}, screens={Screens}",
            customer.Id, customer.CompanyName, req.Plan, req.ScreenCount);

        return Results.Ok(new { checkoutUrl = session.Url, statusToken = customer.Id });
    }

    private static async Task<IResult> HandleStatus(
        Guid token,
        OrchestratorDbContext db)
    {
        var customer = await db.Customers
            .AsNoTracking()
            .FirstOrDefaultAsync(c => c.Id == token);

        if (customer is null)
            return Results.NotFound();

        // Find latest provisioning job if any
        var job = await db.Jobs
            .AsNoTracking()
            .Where(j => j.CustomerId == customer.Id && j.JobType == "provision")
            .OrderByDescending(j => j.CreatedAt)
            .FirstOrDefaultAsync();

        int pctComplete = customer.Status switch
        {
            CustomerStatus.PendingPayment => 0,
            CustomerStatus.Provisioning => job?.Status switch
            {
                JobStatus.Running => 50,
                JobStatus.Completed => 100,
                _ => 10,
            },
            CustomerStatus.Active => 100,
            _ => 0,
        };

        return Results.Ok(new
        {
            status = customer.Status.ToString(),
            provisioningStep = job?.Steps
                .Where(s => s.Status == JobStepStatus.Running)
                .Select(s => s.StepName)
                .FirstOrDefault() ?? (customer.Status == CustomerStatus.Active ? "complete" : "waiting"),
            pctComplete,
        });
    }
}

public record SignupRequest(
    string? CompanyName,
    string? AdminFirstName,
    string? AdminLastName,
    string? AdminEmail,
    string? Phone,
    string? Plan,
    int ScreenCount,
    string? BillingFrequency,
    string? PromoCode);
