using System.Security.Claims;
using System.Security.Cryptography;
using FastEndpoints;
using FluentValidation;
using Microsoft.EntityFrameworkCore;
using TrackQrApi.Data;
using TrackQrApi.Features.Auth.Common;
using TrackQrApi.Features.Domains.Common;
using TrackQrApi.Features.Plans.Services;
using TrackQrApi.Models;

namespace TrackQrApi.Features.Domains.Endpoints;

public class AddDomainRequest
{
    public Guid WorkspaceId { get; set; }
    public string Hostname { get; set; } = string.Empty;
}

public class AddDomainValidator : Validator<AddDomainRequest>
{
    public AddDomainValidator()
    {
        RuleFor(x => x.Hostname)
            .NotEmpty().WithMessage("Hostname is required")
            .MaximumLength(255).WithMessage("Hostname must not exceed 255 characters")
            .Matches(@"^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)*$")
            .WithMessage("Invalid hostname format");
    }
}

public class AddDomainEndpoint(AppDbContext db, IPlanLimitsService planLimits)
    : Endpoint<AddDomainRequest, DomainResponse>
{
    public override void Configure()
    {
        Post("/workspaces/{WorkspaceId}/domains");
    }

    public override async Task HandleAsync(AddDomainRequest req, CancellationToken ct)
    {
        var userId = Guid.Parse(User.FindFirstValue(ClaimTypes.NameIdentifier)!);

        // Verify workspace ownership
        var workspaceExists = await db.Workspaces
            .AnyAsync(w => w.Id == req.WorkspaceId && w.OwnerUserId == userId, ct);

        if (!workspaceExists)
        {
            await HttpContext.Response.SendAsync(new MessageResponse("Workspace not found"), 404, cancellation: ct);
            return;
        }

        // Check plan limits
        if (!await planLimits.CanCreateDomainAsync(req.WorkspaceId, ct))
        {
            await HttpContext.Response.SendAsync(
                new MessageResponse("Domain limit reached. Please upgrade your plan to add more custom domains."),
                402,
                cancellation: ct);
            return;
        }

        // Normalize hostname (lowercase, no trailing dots)
        var hostname = req.Hostname.ToLowerInvariant().TrimEnd('.');

        // Check if domain already exists globally
        var domainExists = await db.Domains
            .AnyAsync(d => d.Hostname == hostname, ct);

        if (domainExists)
        {
            await HttpContext.Response.SendAsync(new MessageResponse("Domain is already registered"), 409,
                cancellation: ct);
            return;
        }

        // Generate verification token
        var verificationToken = GenerateVerificationToken();

        var domain = new Domain
        {
            Id = Guid.NewGuid(),
            WorkspaceId = req.WorkspaceId,
            Hostname = hostname,
            Status = DomainStatus.Pending,
            VerificationToken = verificationToken,
            CreatedAt = DateTime.UtcNow
        };

        db.Domains.Add(domain);
        await db.SaveChangesAsync(ct);

        var response = new DomainResponse(
            domain.Id,
            domain.WorkspaceId,
            domain.Hostname,
            domain.Status.ToString(),
            domain.VerificationToken,
            GetVerificationRecord(domain.VerificationToken),
            domain.CreatedAt
        );

        await HttpContext.Response.SendAsync(response, 201, cancellation: ct);
    }

    private static string GenerateVerificationToken()
    {
        var bytes = RandomNumberGenerator.GetBytes(16);
        return $"trakqr-verify-{Convert.ToHexString(bytes).ToLowerInvariant()}";
    }

    private static string GetVerificationRecord(string token)
    {
        return $"TXT _trakqr-verification {token}";
    }
}