package com.social.media.domain.company.entities;

import com.social.media.domain.company.valueobjects.CompanyId;
import com.social.media.domain.company.valueobjects.Address;
import com.social.media.domain.company.valueobjects.Cnpj;
import com.social.media.domain.company.valueobjects.PhoneNumber;
import com.social.media.domain.company.valueobjects.CompanyPlan;
import com.social.media.domain.shared.AggregateRoot;
import com.social.media.domain.shared.valueobjects.Email;

import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.time.LocalDateTime;
import java.util.Objects;

/**
 * Company Aggregate Root
 * 
 * Represents a company/organization that uses the social media management platform.
 * This is the main aggregate root for the Company bounded context following DDD principles.
 * 
 * Key Responsibilities:
 * - Manage company subscription and billing
 * - Control usage limits and quotas  
 * - Maintain company profile and configuration
 * - Handle trial periods and subscription lifecycle
 * - Enforce business rules for resource limits
 * 
 * Domain Events:
 * - CompanyCreated
 * - CompanyPlanUpgraded
 * - SubscriptionExpired
 * - TrialStarted/TrialEnded
 * - QuotaExceeded
 * 
 * Based on Database Schema: core_business.companies
 * 
 * @author Social Media Manager Team
 * @since 2.0.0
 */
@Entity
@Table(name = "companies", schema = "core_business", 
       indexes = {
           @Index(name = "idx_companies_uuid", columnList = "uuid"),
           @Index(name = "idx_companies_cnpj", columnList = "cnpj"),
           @Index(name = "idx_companies_email", columnList = "email"),
           @Index(name = "idx_companies_status", columnList = "status"),
           @Index(name = "idx_companies_plan", columnList = "plan"),
           @Index(name = "idx_companies_subscription_ends", columnList = "subscription_ends_at"),
           @Index(name = "idx_companies_trial_ends", columnList = "trial_ends_at")
       })
@Getter
@Setter(AccessLevel.PRIVATE)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Company extends AggregateRoot<CompanyId> {
    
    // Primary Key - managed by AggregateRoot
    
    // Basic Information
    @Column(name = "uuid", nullable = false, unique = true, updatable = false, length = 36)
    private String uuid;
    
    /**
     * Constructor for creating Company with specific ID
     */
    protected Company(CompanyId id) {
        super(id);
        this.uuid = id.toString();
    }
    
    /**
     * Set id after construction (for JPA)
     */
    @PostLoad
    protected void initializeFromDatabase() {
        if (this.id != null && this.uuid == null) {
            this.uuid = this.id.toString();
        }
    }
    
    @Column(name = "name", nullable = false, length = 200)
    private String name;
    
    @Column(name = "legal_name", length = 200)
    private String legalName;
    
    @Embedded
    @AttributeOverride(name = "value", column = @Column(name = "cnpj", unique = true, length = 18))
    private Cnpj cnpj;
    
    @Embedded
    @AttributeOverride(name = "value", column = @Column(name = "email", nullable = false, unique = true))
    private Email email;
    
    @Embedded
    @AttributeOverride(name = "value", column = @Column(name = "phone", length = 20))
    private PhoneNumber phone;
    
    @Column(name = "website", length = 255)
    private String website;
    
    // Address Information (embedded)
    @Embedded
    private Address address;
    
    // Business Configuration
    @Enumerated(EnumType.STRING)
    @Column(name = "plan", nullable = false)
    private CompanyPlan.PlanType plan = CompanyPlan.PlanType.BASIC;
    
    @Enumerated(EnumType.STRING)
    @Column(name = "status", nullable = false)
    private CompanyStatus status = CompanyStatus.PENDING;
    
    @Column(name = "activity_sector", length = 100)
    private String activitySector;
    
    // Resource Limits
    @Column(name = "max_users")
    private Integer maxUsers = 5;
    
    @Column(name = "max_social_accounts")
    private Integer maxSocialAccounts = 3;
    
    @Column(name = "max_posts_per_month")
    private Integer maxPostsPerMonth = 100;
    
    @Column(name = "max_campaigns")
    private Integer maxCampaigns = 5;
    
    @Column(name = "storage_limit_gb")
    private Integer storageLimitGb = 1;
    
    // Localization and Preferences
    @Column(name = "timezone", nullable = false, length = 50)
    private String timezone = "America/Sao_Paulo";
    
    @Column(name = "locale", nullable = false, length = 10)  
    private String locale = "pt_BR";
    
    @Column(name = "currency", nullable = false, length = 3)
    private String currency = "BRL";
    
    // Subscription Management
    @Column(name = "subscription_starts_at")
    private LocalDateTime subscriptionStartsAt;
    
    @Column(name = "subscription_ends_at")
    private LocalDateTime subscriptionEndsAt;
    
    @Column(name = "trial_ends_at")
    private LocalDateTime trialEndsAt;
    
    @Column(name = "is_trial", nullable = false)
    private Boolean isTrial = true;
    
    @Embedded
    @AttributeOverride(name = "value", column = @Column(name = "billing_email"))
    private Email billingEmail;
    
    // Domain Events would be here (implementing domain event pattern)
    
    public enum CompanyStatus {
        PENDING,     // Newly created, awaiting verification
        ACTIVE,      // Active and operational
        INACTIVE,    // Temporarily disabled
        SUSPENDED,   // Suspended due to violation or non-payment
        ARCHIVED,    // Archived/soft deleted
        DELETED      // Hard deleted (rarely used)
    }
    
    // Factory Methods
    
    /**
     * Creates a new company with basic information
     */
    public static Company create(String name, String description, String contactEmail, 
                               CompanyPlan.PlanType planType, String timezone, String language) {
        var companyId = CompanyId.newCompanyId();
        var company = new Company(companyId);
        
        company.uuid = companyId.toString();
        company.name = name;
        company.email = Email.of(contactEmail);
        company.plan = planType != null ? planType : CompanyPlan.PlanType.BASIC;
        company.timezone = timezone != null ? timezone : "America/Sao_Paulo";
        company.locale = language != null ? language : "pt_BR";
        company.status = CompanyStatus.ACTIVE;
        
        // Set default limits for the plan
        company.setDefaultLimitsForPlan(company.plan);
        
        // TODO: Publish CompanyCreated domain event
        
        return company;
    }
    
    /**
     * Creates a new company with trial subscription
     */
    public static Company createWithTrial(
            String name, 
            String legalName, 
            String cnpj, 
            String email, 
            String phone,
            String activitySector,
            Address address) {
        
        var companyId = CompanyId.newCompanyId();
        var company = new Company(companyId);
        company.name = Objects.requireNonNull(name, "Company name is required");
        company.legalName = legalName;
        company.cnpj = cnpj != null ? Cnpj.of(cnpj) : null;
        company.email = Email.of(email);
        company.phone = phone != null ? PhoneNumber.of(phone) : null;
        company.activitySector = activitySector;
        company.address = address;
        company.status = CompanyStatus.PENDING;
        company.plan = CompanyPlan.PlanType.BASIC;
        company.isTrial = true;
        
        // Set trial period (30 days)
        company.trialEndsAt = LocalDateTime.now().plusDays(30);
        
        // Set default resource limits based on BASIC plan
        company.setDefaultLimitsForPlan(CompanyPlan.PlanType.BASIC);
        
        // TODO: Publish CompanyCreated domain event
        
        return company;
    }
    
    /**
     * Creates a company with immediate paid subscription
     */
    public static Company createWithSubscription(
            String name,
            String legalName,
            String cnpj,
            String email,
            String phone,
            String activitySector,
            Address address,
            CompanyPlan.PlanType planType,
            LocalDateTime subscriptionEndDate) {
        
        var company = createWithTrial(name, legalName, cnpj, email, phone, activitySector, address);
        company.startPaidSubscription(planType, subscriptionEndDate);
        
        return company;
    }
    
    // Business Logic Methods
    
    /**
     * Get the company ID as value object
     */
    public CompanyId getCompanyId() {
        return getId();
    }
    
    /**
     * Get the company name
     */
    public String getName() {
        return name;
    }
    
    /**
     * Get the company plan
     */
    public CompanyPlan getPlan() {
        return CompanyPlan.of(plan);
    }
    
    /**
     * Update basic company information
     */
    public void updateBasicInfo(String name, String legalName, String website, String activitySector) {
        this.name = Objects.requireNonNull(name, "Company name is required");
        this.legalName = legalName;
        this.website = website;
        this.activitySector = activitySector;
        markAsModified();
    }
    
    /**
     * Update contact information
     */
    public void updateContactInfo(String email, String phone, Email billingEmail) {
        this.email = Email.of(email);
        this.phone = phone != null ? PhoneNumber.of(phone) : null;
        this.billingEmail = billingEmail;
        markAsModified();
    }
    
    /**
     * Update address information
     */
    public void updateAddress(Address newAddress) {
        this.address = newAddress;
        markAsModified();
    }
    
    /**
     * Update localization settings
     */
    public void updateLocalization(String timezone, String locale, String currency) {
        this.timezone = Objects.requireNonNull(timezone, "Timezone is required");
        this.locale = Objects.requireNonNull(locale, "Locale is required");
        this.currency = Objects.requireNonNull(currency, "Currency is required");
        markAsModified();
    }
    
    /**
     * Upgrade company plan
     */
    public void upgradePlan(CompanyPlan.PlanType newPlan) {
        if (newPlan.ordinal() <= this.plan.ordinal()) {
            throw new IllegalArgumentException("Can only upgrade to a higher plan");
        }
        
        var oldPlan = this.plan;
        this.plan = newPlan;
        setDefaultLimitsForPlan(newPlan);
        
        // If in trial, convert to paid subscription
        if (isTrial) {
            startPaidSubscription(newPlan, LocalDateTime.now().plusMonths(1));
        }
        
        // TODO: Publish PlanUpgraded domain event
        markAsModified();
    }
    
    /**
     * Downgrade company plan (for administrative purposes)
     */
    public void downgradePlan(CompanyPlan.PlanType newPlan) {
        if (newPlan.ordinal() >= this.plan.ordinal()) {
            throw new IllegalArgumentException("Can only downgrade to a lower plan");
        }
        
        this.plan = newPlan;
        setDefaultLimitsForPlan(newPlan);
        
        // TODO: Publish PlanDowngraded domain event
        markAsModified();
    }
    
    /**
     * Start paid subscription (end trial)
     */
    public void startPaidSubscription(CompanyPlan.PlanType planType, LocalDateTime endDate) {
        this.plan = planType;
        this.isTrial = false;
        this.trialEndsAt = null;
        this.subscriptionStartsAt = LocalDateTime.now();
        this.subscriptionEndsAt = endDate;
        this.status = CompanyStatus.ACTIVE;
        
        setDefaultLimitsForPlan(planType);
        
        // TODO: Publish SubscriptionStarted domain event
        markAsModified();
    }
    
    /**
     * Extend subscription
     */
    public void extendSubscription(LocalDateTime newEndDate) {
        if (newEndDate.isBefore(LocalDateTime.now())) {
            throw new IllegalArgumentException("Subscription end date cannot be in the past");
        }
        
        this.subscriptionEndsAt = newEndDate;
        
        // Reactivate if expired
        if (status == CompanyStatus.SUSPENDED) {
            this.status = CompanyStatus.ACTIVE;
        }
        
        // TODO: Publish SubscriptionExtended domain event
        markAsModified();
    }
    
    /**
     * Set custom resource limits (for enterprise customers)
     */
    public void setCustomLimits(Integer maxUsers, Integer maxSocialAccounts, 
                               Integer maxPostsPerMonth, Integer maxCampaigns, 
                               Integer storageLimitGb) {
        this.maxUsers = maxUsers;
        this.maxSocialAccounts = maxSocialAccounts;
        this.maxPostsPerMonth = maxPostsPerMonth;
        this.maxCampaigns = maxCampaigns;
        this.storageLimitGb = storageLimitGb;
        markAsModified();
    }
    
    /**
     * Suspend company (admin action)
     */
    public void suspend(String reason) {
        this.status = CompanyStatus.SUSPENDED;
        // TODO: Publish CompanySuspended domain event with reason
        markAsModified();
    }
    
    /**
     * Reactivate suspended company
     */
    public void reactivate() {
        if (status != CompanyStatus.SUSPENDED) {
            throw new IllegalStateException("Only suspended companies can be reactivated");
        }
        
        this.status = CompanyStatus.ACTIVE;
        // TODO: Publish CompanyReactivated domain event
        markAsModified();
    }
    
    /**
     * Archive company (soft delete)
     */
    public void archive() {
        this.status = CompanyStatus.ARCHIVED;
        // TODO: Publish CompanyArchived domain event
        markAsModified();
    }
    
    // Query Methods
    
    /**
     * Check if company is currently in trial period
     */
    public boolean isInTrial() {
        return isTrial && trialEndsAt != null && trialEndsAt.isAfter(LocalDateTime.now());
    }
    
    /**
     * Check if subscription is active
     */
    public boolean hasActiveSubscription() {
        if (isInTrial()) {
            return true;
        }
        return !isTrial && subscriptionEndsAt != null && subscriptionEndsAt.isAfter(LocalDateTime.now());
    }
    
    /**
     * Check if subscription has expired
     */
    public boolean isSubscriptionExpired() {
        if (isInTrial()) {
            return trialEndsAt.isBefore(LocalDateTime.now());
        }
        return subscriptionEndsAt != null && subscriptionEndsAt.isBefore(LocalDateTime.now());
    }
    
    /**
     * Check if company is active
     */
    public boolean isActive() {
        return status == CompanyStatus.ACTIVE && hasActiveSubscription();
    }
    
    /**
     * Get days until subscription expires
     */
    public long getDaysUntilExpiration() {
        LocalDateTime expirationDate = isInTrial() ? trialEndsAt : subscriptionEndsAt;
        if (expirationDate == null) {
            return 0;
        }
        return java.time.Duration.between(LocalDateTime.now(), expirationDate).toDays();
    }
    
    // Resource Limit Validation
    
    /**
     * Check if company can add more users
     */
    public boolean canAddUser(int currentUserCount) {
        return currentUserCount < maxUsers;
    }
    
    /**
     * Check if company can add more social accounts
     */
    public boolean canAddSocialAccount(int currentAccountCount) {
        return currentAccountCount < maxSocialAccounts;
    }
    
    /**
     * Check if company can create more posts this month
     */
    public boolean canCreatePost(int currentMonthPosts) {
        return currentMonthPosts < maxPostsPerMonth;
    }
    
    /**
     * Check if company can create more campaigns
     */
    public boolean canCreateCampaign(int currentCampaignCount) {
        return currentCampaignCount < maxCampaigns;
    }
    
    /**
     * Check if company has storage space available
     */
    public boolean hasStorageSpace(long currentStorageBytes, long newFileBytes) {
        long maxStorageBytes = storageLimitGb * 1024L * 1024L * 1024L; // Convert GB to bytes
        return (currentStorageBytes + newFileBytes) <= maxStorageBytes;
    }
    
    // Private Helper Methods
    
    private void setDefaultLimitsForPlan(CompanyPlan.PlanType planType) {
        switch (planType) {
            case BASIC -> {
                this.maxUsers = 1;
                this.maxSocialAccounts = 3;
                this.maxPostsPerMonth = 50;
                this.maxCampaigns = 5;
                this.storageLimitGb = 1;
            }
            case PROFESSIONAL -> {
                this.maxUsers = 3;
                this.maxSocialAccounts = 10;
                this.maxPostsPerMonth = 200;
                this.maxCampaigns = 15;
                this.storageLimitGb = 5;
            }
            case ENTERPRISE -> {
                this.maxUsers = 10;
                this.maxSocialAccounts = 50;
                this.maxPostsPerMonth = 1000;
                this.maxCampaigns = 50;
                this.storageLimitGb = 20;
            }
        }
    }
    
    /**
     * Get effective maximum posts per month
     */
    public int getEffectiveMaxPostsPerMonth() {
        return maxPostsPerMonth;
    }
    
    /**
     * Get effective maximum users
     */
    public int getEffectiveMaxUsers() {
        return maxUsers;
    }
    
    /**
     * Get company UUID as string
     */
    public String getCompanyUuid() {
        return uuid;
    }
    
    /**
     * Get contact email
     */
    public String getContactEmail() {
        return email != null ? email.getValue() : null;
    }
    
    /**
     * Set website URL
     */
    public void setWebsiteUrl(String websiteUrl) {
        this.website = websiteUrl;
        markAsModified();
    }
    
    private void markAsModified() {
        // This would trigger domain events and update audit fields
        // Implementation depends on your domain event infrastructure
    }
    
    @Override
    public String toString() {
        return String.format("Company{id=%s, uuid='%s', name='%s', plan=%s, status=%s, active=%s}", 
            getId(), uuid, name, plan, status, isActive());
    }
}
