package com.social.media.application.service;

import com.social.media.domain.company.Company;
import com.social.media.domain.company.Company.BranchType;
import com.social.media.domain.company.Company.BranchStatus;
import com.social.media.infrastructure.persistence.repository.CompanyEntityJpaRepository;
import com.social.media.infrastructure.persistence.entity.CompanyEntity;
import com.social.media.infrastructure.persistence.entity.BranchTypeEntity;
import com.social.media.infrastructure.persistence.entity.BranchStatusEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Optional;
import java.util.UUID;

/**
 * Service for managing company hierarchy operations
 * Handles matriz-filial relationships and hierarchical queries
 */
@Service
@Transactional
public class CompanyHierarchyService {
    
    @Autowired
    private CompanyEntityJpaRepository companyRepository;
    
    // === HIERARCHY CREATION ===
    
    /**
     * Create a new matriz company
     */
    public CompanyEntity createMatriz(String name, String email, String cnpj, String region) {
        CompanyEntity matriz = new CompanyEntity();
        matriz.setName(name);
        matriz.setEmail(email);
        matriz.setCnpj(cnpj);
        matriz.setRegion(region);
        matriz.setBranchType(BranchTypeEntity.MATRIZ);
        matriz.setHierarchyLevel(0);
        matriz.setBranchStatus(BranchStatusEntity.ACTIVE);
        
        return companyRepository.save(matriz);
    }
    
    /**
     * Create a new filial under a parent company
     */
    public CompanyEntity createFilial(Long parentId, String name, String email, 
                                    String cnpj, String branchCode, String region, 
                                    BranchTypeEntity branchType) {
        CompanyEntity parent = companyRepository.findById(parentId)
            .orElseThrow(() -> new RuntimeException("Parent company not found"));
        
        CompanyEntity filial = new CompanyEntity();
        filial.setName(name);
        filial.setEmail(email);
        filial.setCnpj(cnpj);
        filial.setBranchCode(branchCode);
        filial.setRegion(region);
        filial.setBranchType(branchType != null ? branchType : BranchTypeEntity.FILIAL);
        filial.setParentCompany(parent);
        filial.setHierarchyLevel(parent.getHierarchyLevel() + 1);
        filial.setBranchStatus(BranchStatusEntity.ACTIVE);
        
        return companyRepository.save(filial);
    }
    
    // === HIERARCHY QUERIES ===
    
    /**
     * Get all matriz companies
     */
    @Transactional(readOnly = true)
    public List<CompanyEntity> getAllMatrizCompanies() {
        return companyRepository.findMatrizCompanies();
    }
    
    /**
     * Get direct children of a company
     */
    @Transactional(readOnly = true)
    public List<CompanyEntity> getDirectChildren(Long companyId) {
        return companyRepository.findDirectChildren(companyId);
    }
    
    /**
     * Get companies by hierarchy level
     */
    @Transactional(readOnly = true)
    public List<CompanyEntity> getCompaniesByHierarchyLevel(Integer level) {
        return companyRepository.findByHierarchyLevel(level);
    }
    
    /**
     * Get companies by region
     */
    @Transactional(readOnly = true)
    public List<CompanyEntity> getCompaniesByRegion(String region) {
        return companyRepository.findByRegion(region);
    }
    
    /**
     * Get companies by branch type
     */
    @Transactional(readOnly = true)
    public List<CompanyEntity> getCompaniesByBranchType(BranchTypeEntity branchType) {
        return companyRepository.findByBranchType(branchType);
    }
    
    /**
     * Search companies with filters
     */
    @Transactional(readOnly = true)
    public List<CompanyEntity> searchCompanies(BranchTypeEntity branchType, String region, 
                                             BranchStatusEntity branchStatus, Integer minLevel, Integer maxLevel) {
        return companyRepository.findCompaniesByFilters(branchType, region, branchStatus, minLevel, maxLevel);
    }
    
    // === HIERARCHY MODIFICATIONS ===
    
    /**
     * Move a filial to a different parent
     */
    public CompanyEntity moveFilial(Long filialId, Long newParentId) {
        CompanyEntity filial = companyRepository.findById(filialId)
            .orElseThrow(() -> new RuntimeException("Filial not found"));
        
        if (filial.getBranchType() == BranchTypeEntity.MATRIZ) {
            throw new RuntimeException("Cannot move a matriz company");
        }
        
        CompanyEntity newParent = companyRepository.findById(newParentId)
            .orElseThrow(() -> new RuntimeException("New parent company not found"));
        
        // Prevent circular references
        if (isDescendantOf(newParent, filial)) {
            throw new RuntimeException("Cannot move company to its own descendant");
        }
        
        filial.setParentCompany(newParent);
        filial.setHierarchyLevel(newParent.getHierarchyLevel() + 1);
        
        return companyRepository.save(filial);
    }
    
    /**
     * Promote filial to independent matriz
     */
    public CompanyEntity promoteToMatriz(Long filialId) {
        CompanyEntity filial = companyRepository.findById(filialId)
            .orElseThrow(() -> new RuntimeException("Filial not found"));
        
        if (filial.getBranchType() == BranchTypeEntity.MATRIZ) {
            throw new RuntimeException("Company is already a matriz");
        }
        
        filial.setParentCompany(null);
        filial.setBranchType(BranchTypeEntity.MATRIZ);
        filial.setHierarchyLevel(0);
        
        return companyRepository.save(filial);
    }
    
    /**
     * Change branch status
     */
    public CompanyEntity changeBranchStatus(Long companyId, BranchStatusEntity newStatus) {
        CompanyEntity company = companyRepository.findById(companyId)
            .orElseThrow(() -> new RuntimeException("Company not found"));
        
        company.setBranchStatus(newStatus);
        return companyRepository.save(company);
    }
    
    // === HIERARCHY STATISTICS ===
    
    /**
     * Get hierarchy statistics for a company
     */
    @Transactional(readOnly = true)
    public HierarchyStats getHierarchyStats(Long companyId) {
        // Get the company and its children
        CompanyEntity company = companyRepository.findById(companyId)
            .orElseThrow(() -> new RuntimeException("Company not found"));
        
        List<CompanyEntity> allChildren = getAllDescendants(companyId);
        
        int totalCompanies = allChildren.size() + 1; // +1 for the company itself
        int activeCompanies = (int) allChildren.stream()
            .filter(c -> c.getBranchStatus() == BranchStatusEntity.ACTIVE)
            .count();
        
        if (company.getBranchStatus() == BranchStatusEntity.ACTIVE) {
            activeCompanies++;
        }
        
        int maxDepth = allChildren.stream()
            .mapToInt(CompanyEntity::getHierarchyLevel)
            .max()
            .orElse(company.getHierarchyLevel());
            
        int totalLevels = (int) allChildren.stream()
            .mapToInt(CompanyEntity::getHierarchyLevel)
            .distinct()
            .count() + 1; // +1 for the company itself level
        
        return new HierarchyStats(totalCompanies, activeCompanies, maxDepth, totalLevels);
    }
    
    /**
     * Get regional distribution
     */
    @Transactional(readOnly = true)
    public List<Object[]> getRegionalDistribution() {
        return companyRepository.countCompaniesByRegion();
    }
    
    // === HELPER METHODS ===
    
    /**
     * Check if company A is descendant of company B
     */
    private boolean isDescendantOf(CompanyEntity potentialDescendant, CompanyEntity potentialAncestor) {
        CompanyEntity current = potentialDescendant;
        while (current.getParentCompany() != null) {
            current = current.getParentCompany();
            if (current.getId().equals(potentialAncestor.getId())) {
                return true;
            }
        }
        return false;
    }
    
    /**
     * Get all descendants recursively
     */
    private List<CompanyEntity> getAllDescendants(Long companyId) {
        List<CompanyEntity> descendants = companyRepository.findDirectChildren(companyId);
        List<CompanyEntity> allDescendants = new java.util.ArrayList<>(descendants);
        
        for (CompanyEntity child : descendants) {
            allDescendants.addAll(getAllDescendants(child.getId()));
        }
        
        return allDescendants;
    }
    
    // === INNER CLASSES ===
    
    public static class HierarchyStats {
        private final int totalCompanies;
        private final int activeCompanies;
        private final int maxDepth;
        private final int totalLevels;
        
        public HierarchyStats(int totalCompanies, int activeCompanies, 
                             int maxDepth, int totalLevels) {
            this.totalCompanies = totalCompanies;
            this.activeCompanies = activeCompanies;
            this.maxDepth = maxDepth;
            this.totalLevels = totalLevels;
        }
        
        // Getters
        public int getTotalCompanies() { return totalCompanies; }
        public int getActiveCompanies() { return activeCompanies; }
        public int getMaxDepth() { return maxDepth; }
        public int getTotalLevels() { return totalLevels; }
        public int getInactiveCompanies() { return totalCompanies - activeCompanies; }
        public double getActivePercentage() { 
            return totalCompanies > 0 ? (double) activeCompanies / totalCompanies * 100 : 0; 
        }
    }
}
