using System.Text.Json;
using Microsoft.EntityFrameworkCore;
using Tiku.Application.Commerce;
using Tiku.Domain.Commerce;

namespace Tiku.Infrastructure.Commerce;

internal sealed partial class CommerceAdminService
{
    public async Task<TenantReconciliationBatchList> GetReconciliationBatchesAsync(
        CommerceAdminActor actor,
        CommerceAdminQuery query,
        CancellationToken cancellationToken = default)
    {
        await AssertAdminAsync(actor, cancellationToken);
        var batches = dbContext.CommerceReconciliationBatches.AsNoTracking()
            .Where(item => item.TenantId == actor.TenantId);
        if (!string.IsNullOrWhiteSpace(query.Provider))
        {
            var provider = NormalizeProvider(query.Provider);
            batches = batches.Where(item => item.Provider == provider);
        }

        if (!string.IsNullOrWhiteSpace(query.Status))
            batches = batches.Where(item => item.Status == ParseReconciliationBatchStatus(query.Status));

        var items = await batches.OrderByDescending(item => item.CreatedAt)
            .Take(Math.Clamp(query.Limit ?? 50, 1, 200))
            .ToArrayAsync(cancellationToken);
        return new TenantReconciliationBatchList(items);
    }

    public async Task<CommerceReconciliationBatch> CreateReconciliationBatchAsync(
        CommerceAdminActor actor,
        CreateReconciliationBatchCommand command,
        CancellationToken cancellationToken = default)
    {
        await AssertAdminAsync(actor, cancellationToken);
        var batch = new CommerceReconciliationBatch
        {
            TenantId = actor.TenantId,
            CreatedBy = actor.UserId,
            Provider = NormalizeProvider(command.Provider),
            BillDate = command.BillDate,
            BillType = command.BillType,
            Source = command.Source,
            SourceName = command.SourceName?.Trim(),
            SourceHash = command.SourceHash.Trim(),
            Status = ReconciliationBatchStatus.Pending,
            Metadata = JsonObjectOrDefault(command.Metadata)
        };
        dbContext.CommerceReconciliationBatches.Add(batch);
        await AddAuditAsync(actor, "commerce.reconciliation_batch.created", "commerce_reconciliation_batches", batch.Id,
            new { batch.Provider, batch.BillDate }, cancellationToken);
        await dbContext.SaveChangesAsync(cancellationToken);
        return batch;
    }

    public async Task<TenantReconciliationIssueList> GetReconciliationIssuesAsync(
        CommerceAdminActor actor,
        CommerceAdminQuery query,
        CancellationToken cancellationToken = default)
    {
        await AssertAdminAsync(actor, cancellationToken);
        var issues = dbContext.CommerceReconciliationIssues.AsNoTracking()
            .Where(item => item.TenantId == actor.TenantId);
        if (!string.IsNullOrWhiteSpace(query.Provider))
        {
            var provider = NormalizeProvider(query.Provider);
            issues = issues.Where(item => item.Provider == provider);
        }

        if (!string.IsNullOrWhiteSpace(query.Status))
            issues = issues.Where(item => item.Status == ParseReconciliationIssueStatus(query.Status));

        var items = await issues.OrderByDescending(item => item.CreatedAt)
            .Take(Math.Clamp(query.Limit ?? 50, 1, 200))
            .ToArrayAsync(cancellationToken);
        return new TenantReconciliationIssueList(items);
    }

    public async Task<CommerceReconciliationIssue> UpdateReconciliationIssueAsync(
        CommerceAdminActor actor,
        UpdateReconciliationIssueCommand command,
        CancellationToken cancellationToken = default)
    {
        await AssertAdminAsync(actor, cancellationToken);
        var issue = await dbContext.CommerceReconciliationIssues.SingleOrDefaultAsync(
                        item => item.TenantId == actor.TenantId && item.Id == command.IssueId,
                        cancellationToken) ??
                    throw new CommerceException("Reconciliation issue was not found.",
                        "reconciliation_issue_not_found");
        var fromStatus = issue.Status;
        issue.Status = command.Status;
        issue.ResolutionType = command.ResolutionType;
        issue.ResolutionNote = command.Note?.Trim();
        issue.AssignedTo = command.AssignedTo ?? issue.AssignedTo;
        if (command.Status is ReconciliationIssueStatus.Resolved or ReconciliationIssueStatus.Ignored)
        {
            issue.ResolvedBy = actor.UserId;
            issue.ResolvedAt = DateTimeOffset.UtcNow;
        }

        dbContext.CommerceReconciliationIssueEvents.Add(new CommerceReconciliationIssueEvent
        {
            TenantId = actor.TenantId,
            IssueId = issue.Id,
            FromStatus = fromStatus,
            ToStatus = command.Status,
            EventType = "status_changed",
            ActorUserId = actor.UserId,
            Note = command.Note,
            Details = JsonSerializer.SerializeToElement(new { command.ResolutionType, command.AssignedTo })
        });
        await AddAuditAsync(actor, "commerce.reconciliation_issue.status_changed", "commerce_reconciliation_issues",
            issue.Id, new { issue.IssueNo, From = fromStatus, To = command.Status }, cancellationToken);
        await dbContext.SaveChangesAsync(cancellationToken);
        return issue;
    }
}