using CashLineERP.Domain.Common;
using CashLineERP.SharedKernel.Documents;

namespace CashLineERP.Domain.Documents;

public sealed class DocumentLink : Entity
{
    public long Id { get; private set; }

    public string ModuleCode { get; private set; } = string.Empty;

    public string EntityName { get; private set; } = string.Empty;

    public long EntityId { get; private set; }

    public string DocumentType { get; private set; } = string.Empty;

    public string AttachmentCategoryCode { get; private set; } = string.Empty;

    public string FileName { get; private set; } = string.Empty;

    public string StorageReference { get; private set; } = string.Empty;

    public string StorageProviderCode { get; private set; } = string.Empty;

    public string? ContentType { get; private set; }

    public int? FileSizeBytes { get; private set; }

    public string? ChecksumText { get; private set; }

    public string LifecycleStatusCode { get; private set; } = DocumentLifecycleStatusCode.Active;

    public int VersionNumber { get; private set; } = 1;

    public long? SupersededByDocumentLinkId { get; private set; }

    public DateTime? RetentionUntilUtc { get; private set; }

    public DateTime? ArchivedAtUtc { get; private set; }

    public long? ArchivedByUserId { get; private set; }

    public string? LifecycleNotesText { get; private set; }

    public long? UploadedByUserId { get; private set; }

    public DateTime UploadedAtUtc { get; private set; } = DateTime.UtcNow;

    public DocumentLink(
        string moduleCode,
        string entityName,
        long entityId,
        string documentType,
        string attachmentCategoryCode,
        string fileName,
        string storageReference,
        string storageProviderCode,
        string? contentType = null,
        int? fileSizeBytes = null,
        string? checksumText = null,
        DateTime? retentionUntilUtc = null,
        int versionNumber = 1,
        long? uploadedByUserId = null)
    {
        if (entityId <= 0)
        {
            throw new InvalidOperationException("Document link requires a valid entity identifier.");
        }

        if (versionNumber <= 0)
        {
            throw new InvalidOperationException("Document link version number must be greater than zero.");
        }

        if (fileSizeBytes.HasValue && fileSizeBytes.Value < 0)
        {
            throw new InvalidOperationException("Document link file size cannot be negative.");
        }

        ModuleCode = NormalizeRequired(moduleCode, "module code");
        EntityName = NormalizeRequired(entityName, "entity name");
        EntityId = entityId;
        DocumentType = NormalizeRequired(documentType, "document type");
        AttachmentCategoryCode = NormalizeRequired(attachmentCategoryCode, "attachment category code");
        FileName = NormalizeRequired(fileName, "file name");
        StorageReference = NormalizeRequired(storageReference, "storage reference");
        StorageProviderCode = NormalizeRequired(storageProviderCode, "storage provider code");
        ContentType = contentType;
        FileSizeBytes = fileSizeBytes;
        ChecksumText = NormalizeOptional(checksumText);
        RetentionUntilUtc = retentionUntilUtc;
        VersionNumber = versionNumber;
        UploadedByUserId = uploadedByUserId;
        CreatedByUserId = uploadedByUserId;
    }

    public void Archive(long actorUserId, string? reasonText)
    {
        EnsureLifecycleStatus(DocumentLifecycleStatusCode.Active, "Only active documents can be archived.");

        ArchivedAtUtc = DateTime.UtcNow;
        ArchivedByUserId = actorUserId;
        LifecycleStatusCode = DocumentLifecycleStatusCode.Archived;
        LifecycleNotesText = NormalizeOptional(reasonText);
        Touch(actorUserId);
    }

    public void MarkSuperseded(long actorUserId, long supersededByDocumentLinkId, string? reasonText)
    {
        EnsureLifecycleStatus(DocumentLifecycleStatusCode.Active, "Only active documents can be superseded.");

        if (supersededByDocumentLinkId <= 0)
        {
            throw new InvalidOperationException("Superseding document link identifier must be valid.");
        }

        LifecycleStatusCode = DocumentLifecycleStatusCode.Superseded;
        SupersededByDocumentLinkId = supersededByDocumentLinkId;
        LifecycleNotesText = NormalizeOptional(reasonText);
        Touch(actorUserId);
    }

    private DocumentLink()
    {
    }

    private void EnsureLifecycleStatus(string expectedStatusCode, string errorMessage)
    {
        if (!string.Equals(LifecycleStatusCode, expectedStatusCode, StringComparison.OrdinalIgnoreCase))
        {
            throw new InvalidOperationException(errorMessage);
        }
    }

    private static string NormalizeRequired(string value, string fieldName)
    {
        var normalizedValue = NormalizeOptional(value);
        if (string.IsNullOrWhiteSpace(normalizedValue))
        {
            throw new InvalidOperationException($"Document link {fieldName} is required.");
        }

        return normalizedValue;
    }

    private static string? NormalizeOptional(string? value) =>
        string.IsNullOrWhiteSpace(value) ? null : value.Trim();
}
