using Books.Api.Domain.Attachments.Events;
using EventFlow.Aggregates;

namespace Books.Api.Domain.Attachments;

/// <summary>
/// Aggregate for managing document attachments (bilag).
/// Required by Bogføringsloven § 6 - documents must be retained for 5 years.
/// </summary>
public class AttachmentAggregate(AttachmentId id)
    : AggregateRoot<AttachmentAggregate, AttachmentId>(id),
    IEmit<AttachmentUploadedEvent>,
    IEmit<AttachmentLinkedToTransactionEvent>,
    IEmit<AttachmentDeletedEvent>
{
    private bool _isCreated;
    private bool _isDeleted;
    private string _companyId = string.Empty;
    private string? _transactionId;
    private DateTimeOffset _uploadedAt;

    public string CompanyId => _companyId;
    public bool IsDeleted => _isDeleted;

    #region Apply Methods

    public void Apply(AttachmentUploadedEvent e)
    {
        _isCreated = true;
        _companyId = e.CompanyId;
        _transactionId = e.TransactionId;
        _uploadedAt = DateTimeOffset.UtcNow;
    }

    public void Apply(AttachmentLinkedToTransactionEvent e)
    {
        _transactionId = e.TransactionId;
    }

    public void Apply(AttachmentDeletedEvent e)
    {
        _isDeleted = true;
    }

    #endregion

    #region Command Methods

    /// <summary>
    /// Upload a new attachment.
    /// </summary>
    public void Upload(
        string companyId,
        string fileName,
        string originalFileName,
        string contentType,
        long fileSize,
        string storagePath,
        string uploadedBy,
        string? draftId = null,
        string? transactionId = null)
    {
        if (_isCreated)
            throw new DomainException(
                "ATTACHMENT_ALREADY_EXISTS",
                "Attachment already exists",
                "Bilag eksisterer allerede");

        if (string.IsNullOrWhiteSpace(companyId))
            throw new DomainException(
                "COMPANY_ID_REQUIRED",
                "Company ID is required",
                "Virksomheds-ID er påkrævet");

        if (string.IsNullOrWhiteSpace(fileName))
            throw new DomainException(
                "FILE_NAME_REQUIRED",
                "File name is required",
                "Filnavn er påkrævet");

        if (string.IsNullOrWhiteSpace(contentType))
            throw new DomainException(
                "CONTENT_TYPE_REQUIRED",
                "Content type is required",
                "Filtype er påkrævet");

        if (fileSize <= 0)
            throw new DomainException(
                "INVALID_FILE_SIZE",
                "File size must be greater than 0",
                "Filstørrelse skal være større end 0");

        if (string.IsNullOrWhiteSpace(storagePath))
            throw new DomainException(
                "STORAGE_PATH_REQUIRED",
                "Storage path is required",
                "Lagringssti er påkrævet");

        if (string.IsNullOrWhiteSpace(uploadedBy))
            throw new DomainException(
                "UPLOADED_BY_REQUIRED",
                "Uploaded by is required",
                "Uploadet af er påkrævet");

        // Validate allowed file types
        var allowedTypes = new[]
        {
            "application/pdf",
            "image/png", "image/jpeg", "image/gif", "image/webp",
            "application/msword",
            "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
            "application/vnd.ms-excel",
            "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
        };

        if (!allowedTypes.Contains(contentType.ToLowerInvariant()))
            throw new DomainException(
                "INVALID_FILE_TYPE",
                $"File type '{contentType}' is not allowed. Allowed types: PDF, images, Word, Excel",
                $"Filtypen '{contentType}' er ikke tilladt. Tilladte typer: PDF, billeder, Word, Excel");

        Emit(new AttachmentUploadedEvent(
            companyId.Trim(),
            fileName.Trim(),
            originalFileName.Trim(),
            contentType.ToLowerInvariant(),
            fileSize,
            storagePath.Trim(),
            uploadedBy,
            draftId?.Trim(),
            transactionId?.Trim()));
    }

    /// <summary>
    /// Link attachment to a posted transaction.
    /// Called after draft is posted to ledger.
    /// </summary>
    public void LinkToTransaction(string transactionId)
    {
        EnsureExists();

        if (string.IsNullOrWhiteSpace(transactionId))
            throw new DomainException(
                "TRANSACTION_ID_REQUIRED",
                "Transaction ID is required",
                "Transaktions-ID er påkrævet");

        if (!string.IsNullOrEmpty(_transactionId))
            throw new DomainException(
                "ALREADY_LINKED",
                "Attachment is already linked to a transaction",
                "Bilaget er allerede knyttet til en transaktion");

        Emit(new AttachmentLinkedToTransactionEvent(transactionId.Trim()));
    }

    /// <summary>
    /// Delete an attachment.
    /// Note: Per Bogføringsloven § 6, this should only be used for
    /// administrative cleanup after the 5-year retention period.
    /// </summary>
    public void Delete(string deletedBy, string reason)
    {
        EnsureExists();

        if (string.IsNullOrWhiteSpace(deletedBy))
            throw new DomainException(
                "DELETED_BY_REQUIRED",
                "Deleted by is required",
                "Slettet af er påkrævet");

        if (string.IsNullOrWhiteSpace(reason))
            throw new DomainException(
                "DELETE_REASON_REQUIRED",
                "A reason for deletion is required (Bogføringsloven § 6)",
                "En begrundelse for sletning er påkrævet (Bogføringsloven § 6)");

        // Check retention period
        var retentionYears = 5;
        var retentionEndDate = _uploadedAt.AddYears(retentionYears);

        if (DateTimeOffset.UtcNow < retentionEndDate && !string.IsNullOrEmpty(_transactionId))
            throw new DomainException(
                "RETENTION_PERIOD_ACTIVE",
                $"Cannot delete attachment linked to transaction - retention period ends {retentionEndDate:yyyy-MM-dd} (Bogføringsloven § 6)",
                $"Kan ikke slette bilag knyttet til transaktion - opbevaringsperiode udløber {retentionEndDate:dd-MM-yyyy} (Bogføringsloven § 6)");

        Emit(new AttachmentDeletedEvent(deletedBy, reason.Trim()));
    }

    #endregion

    #region Private Methods

    private void EnsureExists()
    {
        if (!_isCreated)
            throw new DomainException(
                "ATTACHMENT_NOT_FOUND",
                "Attachment does not exist",
                "Bilaget findes ikke");

        if (_isDeleted)
            throw new DomainException(
                "ATTACHMENT_DELETED",
                "Attachment has been deleted",
                "Bilaget er blevet slettet");
    }

    #endregion
}
