using CashLineERP.Application.Parties;
using CashLineERP.Domain.Parties;
using CashLineERP.Domain.Workflow;
using Microsoft.EntityFrameworkCore;

namespace CashLineERP.Infrastructure.Persistence.Repositories;

public sealed class OnboardingReviewRepository : IOnboardingReviewRepository
{
    private readonly CashLineErpDbContext _dbContext;

    public OnboardingReviewRepository(CashLineErpDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public OnboardingReviewCaseView GetReviewCase(string subjectTypeCode, long subjectProfileId)
    {
        var workflowInstance = ResolveWorkflowInstance(subjectTypeCode, subjectProfileId);
        return BuildCaseView(subjectTypeCode, subjectProfileId, workflowInstance);
    }

    public OnboardingReviewCaseView UpsertChecklistItems(string subjectTypeCode, long subjectProfileId, long actorUserId, IReadOnlyCollection<UpsertOnboardingReviewChecklistItemCommand> items)
    {
        var workflowInstance = ResolveWorkflowInstance(subjectTypeCode, subjectProfileId);

        foreach (var item in items)
        {
            var existing = _dbContext.OnboardingReviewChecklistItems.SingleOrDefault(entry =>
                entry.WorkflowInstanceId == workflowInstance.Id &&
                entry.ChecklistItemCode == item.ChecklistItemCode);

            if (existing is null)
            {
                _dbContext.OnboardingReviewChecklistItems.Add(new OnboardingReviewChecklistItem(
                    workflowInstance.Id,
                    subjectTypeCode,
                    subjectProfileId,
                    item.ChecklistItemCode,
                    item.DisplayName,
                    item.StatusCode,
                    item.CoverageCode,
                    actorUserId,
                    item.ReviewerNotes));
            }
            else
            {
                existing.UpdateReview(item.DisplayName, item.StatusCode, item.CoverageCode, actorUserId, item.ReviewerNotes);
            }
        }

        _dbContext.SaveChanges();
        return BuildCaseView(subjectTypeCode, subjectProfileId, workflowInstance);
    }

    public OnboardingReviewCaseView AddFinding(string subjectTypeCode, long subjectProfileId, long actorUserId, CreateOnboardingReviewFindingCommand finding)
    {
        var workflowInstance = ResolveWorkflowInstance(subjectTypeCode, subjectProfileId);
        _dbContext.OnboardingReviewFindings.Add(new OnboardingReviewFinding(
            workflowInstance.Id,
            subjectTypeCode,
            subjectProfileId,
            finding.FindingCode,
            finding.SeverityCode,
            finding.RelatedItemCode,
            finding.StatusCode,
            finding.FindingText,
            actorUserId));
        _dbContext.SaveChanges();
        return BuildCaseView(subjectTypeCode, subjectProfileId, workflowInstance);
    }

    private OnboardingReviewCaseView BuildCaseView(string subjectTypeCode, long subjectProfileId, WorkflowInstance workflowInstance)
    {
        var checklistItems = _dbContext.OnboardingReviewChecklistItems.AsNoTracking()
            .Where(entry => entry.WorkflowInstanceId == workflowInstance.Id)
            .OrderBy(entry => entry.ChecklistItemCode)
            .Select(entry => new OnboardingReviewChecklistItemView(entry.Id, entry.ChecklistItemCode, entry.DisplayName, entry.StatusCode, entry.CoverageCode, entry.ReviewerNotesText, entry.ReviewedAtUtc, entry.ReviewedByUserId))
            .ToArray();

        var findings = _dbContext.OnboardingReviewFindings.AsNoTracking()
            .Where(entry => entry.WorkflowInstanceId == workflowInstance.Id)
            .OrderBy(entry => entry.LoggedAtUtc)
            .Select(entry => new OnboardingReviewFindingView(entry.Id, entry.FindingCode, entry.SeverityCode, entry.RelatedItemCode, entry.StatusCode, entry.FindingText, entry.LoggedAtUtc, entry.LoggedByUserId))
            .ToArray();

        return new OnboardingReviewCaseView(subjectTypeCode, subjectProfileId, workflowInstance.Id, workflowInstance.CurrentStatusCode, checklistItems, findings);
    }

    private WorkflowInstance ResolveWorkflowInstance(string subjectTypeCode, long subjectProfileId)
    {
        if (subjectProfileId <= 0)
        {
            throw new InvalidOperationException("Onboarding review requires a valid subject profile identifier.");
        }

        var documentType = subjectTypeCode switch
        {
            "SUPPLIER" => "SUPPLIER_PROFILE",
            "CUSTOMER" => "CUSTOMER_PROFILE",
            _ => throw new InvalidOperationException("Unsupported onboarding subject type.")
        };

        var exists = subjectTypeCode switch
        {
            "SUPPLIER" => _dbContext.SupplierProfiles.AsNoTracking().Any(entry => entry.Id == subjectProfileId),
            "CUSTOMER" => _dbContext.CustomerProfiles.AsNoTracking().Any(entry => entry.Id == subjectProfileId),
            _ => false
        };

        if (!exists)
        {
            throw new InvalidOperationException("Requested onboarding subject profile was not found.");
        }

        var workflowInstance = _dbContext.WorkflowInstances.AsNoTracking().SingleOrDefault(entry => entry.DocumentType == documentType && entry.DocumentId == subjectProfileId);
        if (workflowInstance is null)
        {
            throw new InvalidOperationException("Onboarding review requires a submitted onboarding workflow instance.");
        }

        return workflowInstance;
    }
}
