using System.Security.Claims;
using FastEndpoints;
using FluentValidation;
using Microsoft.EntityFrameworkCore;
using TrackQrApi.Data;
using TrackQrApi.Features.Auth.Common;
using TrackQrApi.Features.Links.Common;
using TrackQrApi.Models;

namespace TrackQrApi.Features.Links.Endpoints;

public class UpdateLinkRequest
{
    public Guid WorkspaceId { get; set; }
    public Guid Id { get; set; }
    public string? DestinationUrl { get; set; }
    public string? Title { get; set; }
    public string? Status { get; set; }
    public DateTime? ExpiresAt { get; set; }
    public string? Password { get; set; }
    public bool? RemovePassword { get; set; }
    public Guid? ProjectId { get; set; }
    public bool? RemoveProject { get; set; }
}

public class UpdateLinkValidator : Validator<UpdateLinkRequest>
{
    public UpdateLinkValidator()
    {
        RuleFor(x => x.DestinationUrl)
            .MaximumLength(2048).WithMessage("Destination URL must not exceed 2048 characters")
            .Must(BeAValidUrl).WithMessage("Destination URL must be a valid URL")
            .When(x => !string.IsNullOrEmpty(x.DestinationUrl));

        RuleFor(x => x.Title)
            .MaximumLength(255).WithMessage("Title must not exceed 255 characters");

        RuleFor(x => x.Status)
            .Must(s => s == null || Enum.TryParse<ShortLinkStatus>(s, true, out _))
            .WithMessage("Status must be 'Active' or 'Disabled'");

        RuleFor(x => x.Password)
            .MinimumLength(4).WithMessage("Password must be at least 4 characters")
            .When(x => !string.IsNullOrEmpty(x.Password));
    }

    private static bool BeAValidUrl(string? url)
    {
        if (string.IsNullOrEmpty(url)) return true;
        return Uri.TryCreate(url, UriKind.Absolute, out var uri)
               && (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps);
    }
}

public class UpdateLinkEndpoint(AppDbContext db)
    : Endpoint<UpdateLinkRequest, LinkResponse>
{
    public override void Configure()
    {
        Put("/workspaces/{WorkspaceId}/links/{Id}");
    }

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

        var link = await db.ShortLinks
            .Include(l => l.Workspace)
            .FirstOrDefaultAsync(
                l => l.Id == req.Id && l.WorkspaceId == req.WorkspaceId && l.Workspace.OwnerUserId == userId, ct);

        if (link is null)
        {
            await HttpContext.Response.SendAsync(new MessageResponse("Link not found"), 404, cancellation: ct);
            return;
        }

        // Verify project belongs to workspace if specified
        if (req.ProjectId.HasValue)
        {
            var projectExists = await db.Projects
                .AnyAsync(p => p.Id == req.ProjectId.Value && p.WorkspaceId == req.WorkspaceId, ct);

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

        // Update fields
        if (!string.IsNullOrEmpty(req.DestinationUrl)) link.DestinationUrl = req.DestinationUrl;

        if (req.Title != null) link.Title = req.Title;

        if (!string.IsNullOrEmpty(req.Status) && Enum.TryParse<ShortLinkStatus>(req.Status, true, out var status))
            link.Status = status;

        if (req.ExpiresAt.HasValue) link.ExpiresAt = req.ExpiresAt.Value;

        if (!string.IsNullOrEmpty(req.Password))
            link.PasswordHash = BCrypt.Net.BCrypt.HashPassword(req.Password);
        else if (req.RemovePassword == true) link.PasswordHash = null;

        if (req.ProjectId.HasValue)
            link.ProjectId = req.ProjectId.Value;
        else if (req.RemoveProject == true) link.ProjectId = null;

        link.UpdatedAt = DateTime.UtcNow;
        await db.SaveChangesAsync(ct);

        var response = new LinkResponse(
            link.Id,
            link.WorkspaceId,
            link.ProjectId,
            link.DomainId,
            link.Slug,
            link.DestinationUrl,
            link.Title,
            link.Status.ToString(),
            link.ExpiresAt,
            link.PasswordHash != null,
            link.CreatedAt,
            link.UpdatedAt
        );

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