namespace KArtSell.Modules.ModelOperations.PortfolioReconciliation;

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using FastEndpoints;
using KArtSell.BuildingBlocks.Time;
using Microsoft.AspNetCore.Http;

/// <summary>
/// GET /reconciliation/holdings - Returns current portfolio holdings
/// </summary>
[DontRegister]
public class GetHoldingsEndpoint : EndpointWithoutRequest<GetHoldingsResponse>
{
    private readonly IReconciliationRepository _repository;

    public GetHoldingsEndpoint(IReconciliationRepository repository)
    {
        _repository = repository ?? throw new ArgumentNullException(nameof(repository));
    }

    public override void Configure()
    {
        Get("/reconciliation/holdings");
        AllowAnonymous();
    }

    public override async Task HandleAsync(CancellationToken ct)
    {
        var holdings = await _repository.GetOpenHoldingsAsync();

        var response = new GetHoldingsResponse
        {
            Items = holdings.ConvertAll(h => new HoldingDto
            {
                Id = h.Id,
                SecurityId = h.SecurityId,
                Quantity = h.Quantity,
                WeightedAvgCost = h.WeightedAvgCost,
                TotalCostBasis = h.TotalCostBasis,
                MarketValue = h.MarketValue,
                UnrealizedGainLoss = h.UnrealizedGainLoss,
                UpdatedAt = h.UpdatedAt,
                CorrelationId = h.CorrelationId
            }),
            Total = holdings.Count,
            Pages = 1
        };

        await Send.OkAsync(response, ct);
    }
}

public class GetHoldingsResponse
{
    public List<HoldingDto> Items { get; set; } = new();
    public int Total { get; set; }
    public int Pages { get; set; }
}

public class HoldingDto
{
    public Guid Id { get; set; }
    public Guid SecurityId { get; set; }
    public int Quantity { get; set; }
    public decimal WeightedAvgCost { get; set; }
    public decimal TotalCostBasis { get; set; }
    public decimal? MarketValue { get; set; }
    public decimal? UnrealizedGainLoss { get; set; }
    public DateTime UpdatedAt { get; set; }
    public Guid CorrelationId { get; set; }
}

/// <summary>
/// GET /reconciliation/mismatches - Returns flagged discrepancies
/// </summary>
[DontRegister]
public class GetMismatchesEndpoint : EndpointWithoutRequest<GetMismatchesResponse>
{
    private readonly IReconciliationRepository _repository;
    private readonly IClock _clock;

    public GetMismatchesEndpoint(IReconciliationRepository repository, IClock clock)
    {
        _repository = repository ?? throw new ArgumentNullException(nameof(repository));
        _clock = clock ?? throw new ArgumentNullException(nameof(clock));
    }

    public override void Configure()
    {
        Get("/reconciliation/mismatches");
        AllowAnonymous();
    }

    public override async Task HandleAsync(CancellationToken ct)
    {
        var now = _clock.UtcNow.UtcDateTime;
        var dateFrom = HttpContext.Request.Query.TryGetValue("dateFrom", out var fromVal)
            ? DateTime.Parse(fromVal.ToString())
            : now.AddDays(-30);

        var dateTo = HttpContext.Request.Query.TryGetValue("dateTo", out var toVal)
            ? DateTime.Parse(toVal.ToString())
            : now;

        var logs = await _repository.GetReconciliationLogsAsync(dateFrom, dateTo);
        var mismatches = logs.Where(l => l.MismatchDetected).ToList();

        var response = new GetMismatchesResponse
        {
            Items = mismatches.ConvertAll(m => new MismatchDto
            {
                Id = m.Id,
                TradeId = m.TradeId,
                HoldingId = m.HoldingId,
                MismatchReason = m.MismatchReason,
                QuantityBefore = m.QuantityBefore,
                QuantityAfter = m.QuantityAfter,
                CostBasisDelta = m.CostBasisDelta,
                DetectedAt = m.ReconciledAt
            }),
            Total = mismatches.Count,
            Pages = 1
        };

        await Send.OkAsync(response, ct);
    }
}

public class GetMismatchesResponse
{
    public List<MismatchDto> Items { get; set; } = new();
    public int Total { get; set; }
    public int Pages { get; set; }
}

public class MismatchDto
{
    public Guid Id { get; set; }
    public Guid TradeId { get; set; }
    public Guid HoldingId { get; set; }
    public string? MismatchReason { get; set; }
    public int QuantityBefore { get; set; }
    public int QuantityAfter { get; set; }
    public decimal CostBasisDelta { get; set; }
    public DateTime DetectedAt { get; set; }
}

/// <summary>
/// POST /reconciliation/reconcile-trade - Trigger trade reconciliation
/// </summary>
[DontRegister]
public class ReconcileTradeEndpoint : Endpoint<ReconcileTradeRequest>
{
    private readonly ReconcileTradeHandler _handler;

    public ReconcileTradeEndpoint(ReconcileTradeHandler handler)
    {
        _handler = handler ?? throw new ArgumentNullException(nameof(handler));
    }

    public override void Configure()
    {
        Post("/reconciliation/reconcile-trade");
        AllowAnonymous();
    }

    public override async Task HandleAsync(ReconcileTradeRequest request, CancellationToken ct)
    {
        if (!ReconcileTradeRequestContract.HasIdempotencyKey(request))
        {
            await Send.ResponseAsync(new
            {
                type = "https://httpstatuses.com/400",
                title = "Invalid reconciliation request",
                detail = "Idempotency-Key is required for replay-safe reconciliation."
            }, StatusCodes.Status400BadRequest, ct);
            return;
        }

        var command = new ReconcileTradeCommand
        {
            TradeId = request.TradeId,
            SecurityId = request.SecurityId,
            ExecutedQuantity = request.ExecutedQuantity,
            ExecutedPrice = request.ExecutedPrice,
            ApprovedQuantity = request.ApprovedQuantity,
            ApprovedPrice = request.ApprovedPrice,
            TradeDate = request.TradeDate,
            ExpectedSettlementDate = request.ExpectedSettlementDate,
            ActualSettlementDate = request.ActualSettlementDate,
            CorrelationId = request.CorrelationId,
            IdempotencyKey = request.IdempotencyKey
        };

        await _handler.HandleAsync(command);

        await Send.NoContentAsync(ct);
    }
}

public static class ReconcileTradeRequestContract
{
    public static bool HasIdempotencyKey(ReconcileTradeRequest request) =>
        request is not null && !string.IsNullOrWhiteSpace(request.IdempotencyKey);
}

public class ReconcileTradeRequest
{
    public Guid TradeId { get; set; }
    public Guid SecurityId { get; set; }
    public int ExecutedQuantity { get; set; }
    public decimal ExecutedPrice { get; set; }
    public int ApprovedQuantity { get; set; }
    public decimal ApprovedPrice { get; set; }
    public DateTime TradeDate { get; set; }
    public DateTime ExpectedSettlementDate { get; set; }
    public DateTime? ActualSettlementDate { get; set; }
    public Guid CorrelationId { get; set; }
    public string? IdempotencyKey { get; set; }
}

/// <summary>
/// GET /reconciliation/report/daily - Returns daily reconciliation report
/// </summary>
[DontRegister]
public class GetDailyReportEndpoint : EndpointWithoutRequest<ReconciliationReportDto>
{
    private readonly ReconciliationEngine _engine;
    private readonly IClock _clock;

    public GetDailyReportEndpoint(ReconciliationEngine engine, IClock clock)
    {
        _engine = engine ?? throw new ArgumentNullException(nameof(engine));
        _clock = clock ?? throw new ArgumentNullException(nameof(clock));
    }

    public override void Configure()
    {
        Get("/reconciliation/report/daily");
        AllowAnonymous();
    }

    public override async Task HandleAsync(CancellationToken ct)
    {
        var reportDate = _clock.UtcNow.UtcDateTime;
        var report = await _engine.GenerateDailyReportAsync(reportDate, Guid.NewGuid());

        var response = new ReconciliationReportDto
        {
            ReportDate = report.ReportDate,
            TotalLogsProcessed = report.TotalLogsProcessed,
            TotalMismatches = report.TotalMismatches,
            MismatchesByHighSeverity = report.MismatchesByHighSeverity,
            MismatchesByMediumSeverity = report.MismatchesByMediumSeverity,
            MismatchPercentage = report.MismatchPercentage,
            GeneratedAt = report.GeneratedAt
        };

        await Send.OkAsync(response, ct);
    }
}

public class ReconciliationReportDto
{
    public DateTime ReportDate { get; set; }
    public int TotalLogsProcessed { get; set; }
    public int TotalMismatches { get; set; }
    public int MismatchesByHighSeverity { get; set; }
    public int MismatchesByMediumSeverity { get; set; }
    public double MismatchPercentage { get; set; }
    public DateTime GeneratedAt { get; set; }
}
