import 'package:dartz/dartz.dart';
import 'package:injectable/injectable.dart';
import '../../core/constants/app_constants.dart';
import '../../core/errors/failures.dart';
import '../../domain/repositories/company_repository.dart';
import '../../models/company_model.dart';
import '../../models/address_model.dart';
import '../datasources/remote/company_remote_datasource.dart';
import '../models/common/paginated_response.dart';
import '../models/company/company_dto.dart';
import '../models/company/branch_dto.dart';
import '../models/company/company_list_dto.dart';

/// 회사 관리 Repository 구현체
/// 회사 및 지점 정보 CRUD 작업을 처리하며 도메인 모델과 API DTO 간 변환을 담당
@Injectable(as: CompanyRepository)
class CompanyRepositoryImpl implements CompanyRepository {
  final CompanyRemoteDataSource remoteDataSource;

  CompanyRepositoryImpl({required this.remoteDataSource});

  @override
  Future<Either<Failure, PaginatedResponse<Company>>> getCompanies({
    int? page,
    int? limit,
    String? search,
    CompanyType? companyType,
    String? sortBy,
    String? sortOrder,
  }) async {
    try {
      final result = await remoteDataSource.getCompanies(
        page: page ?? 1,
        perPage: limit ?? AppConstants.companyPageSize,
        search: search,
        isActive: null, // companyType에 따른 필터링 로직 필요 시 추가
      );
      
      // DTO를 도메인 모델로 변환
      final companies = result.items.map((dto) => _mapDtoToDomain(dto)).toList();
      
      final paginatedResult = PaginatedResponse<Company>(
        items: companies,
        page: result.page,
        size: result.size,
        totalElements: result.totalElements,
        totalPages: result.totalPages,
        first: result.first,
        last: result.last,
      );
      
      return Right(paginatedResult);
    } catch (e) {
      return Left(ServerFailure(
        message: '회사 목록 조회 중 오류가 발생했습니다: ${e.toString()}',
      ));
    }
  }

  @override
  Future<Either<Failure, Company>> getCompanyById(int id) async {
    try {
      final result = await remoteDataSource.getCompanyWithChildren(id);
      final company = _mapDetailDtoToDomain(result);
      return Right(company);
    } catch (e) {
      if (e.toString().contains('404')) {
        return Left(NotFoundFailure(
          message: '해당 회사를 찾을 수 없습니다.',
          resourceType: 'Company',
          resourceId: id.toString(),
        ));
      }
      return Left(ServerFailure(
        message: '회사 상세 정보 조회 중 오류가 발생했습니다: ${e.toString()}',
      ));
    }
  }

  @override
  Future<Either<Failure, Company>> createCompany(Company company) async {
    try {
      final request = _mapDomainToCreateRequest(company);
      final result = await remoteDataSource.createCompany(request);
      final createdCompany = _mapResponseToDomain(result);
      return Right(createdCompany);
    } catch (e) {
      if (e.toString().contains('중복')) {
        return Left(DuplicateFailure(
          message: '이미 존재하는 회사명입니다.',
          field: 'name',
          value: company.name,
        ));
      }
      if (e.toString().contains('유효성')) {
        return Left(ValidationFailure(
          message: '입력 데이터가 올바르지 않습니다.',
        ));
      }
      return Left(ServerFailure(
        message: '회사 생성 중 오류가 발생했습니다: ${e.toString()}',
      ));
    }
  }

  @override
  Future<Either<Failure, Company>> updateCompany(int id, Company company) async {
    try {
      final request = _mapDomainToUpdateRequest(company);
      final result = await remoteDataSource.updateCompany(id, request);
      final updatedCompany = _mapResponseToDomain(result);
      return Right(updatedCompany);
    } catch (e) {
      if (e.toString().contains('404')) {
        return Left(NotFoundFailure(
          message: '수정할 회사를 찾을 수 없습니다.',
          resourceType: 'Company',
          resourceId: id.toString(),
        ));
      }
      if (e.toString().contains('중복')) {
        return Left(DuplicateFailure(
          message: '이미 존재하는 회사명입니다.',
          field: 'name',
          value: company.name,
        ));
      }
      return Left(ServerFailure(
        message: '회사 정보 수정 중 오류가 발생했습니다: ${e.toString()}',
      ));
    }
  }

  @override
  Future<Either<Failure, void>> deleteCompany(int id) async {
    try {
      await remoteDataSource.deleteCompany(id);
      return const Right(null);
    } catch (e) {
      if (e.toString().contains('404')) {
        return Left(NotFoundFailure(
          message: '삭제할 회사를 찾을 수 없습니다.',
          resourceType: 'Company',
          resourceId: id.toString(),
        ));
      }
      if (e.toString().contains('참조')) {
        return Left(BusinessFailure(
          message: '해당 회사에 연결된 데이터가 있어 삭제할 수 없습니다.',
        ));
      }
      return Left(ServerFailure(
        message: '회사 삭제 중 오류가 발생했습니다: ${e.toString()}',
      ));
    }
  }

  @override
  Future<Either<Failure, Company>> restoreCompany(int id) async {
    try {
      final result = await remoteDataSource.restoreCompany(id);
      final restoredCompany = _mapResponseToDomain(result);
      return Right(restoredCompany);
    } catch (e) {
      if (e.toString().contains('404')) {
        return Left(NotFoundFailure(
          message: '복구할 회사를 찾을 수 없습니다.',
          resourceType: 'Company',
          resourceId: id.toString(),
        ));
      }
      return Left(ServerFailure(
        message: '회사 복구 중 오류가 발생했습니다: ${e.toString()}',
      ));
    }
  }

  @override
  Future<Either<Failure, Company>> toggleCompanyStatus(int id) async {
    try {
      // 현재 회사 정보 조회
      final currentCompany = await remoteDataSource.getCompanyDetail(id);
      final newStatus = !currentCompany.isActive;
      
      // 상태 업데이트
      await remoteDataSource.updateCompanyStatus(id, newStatus);
      
      // 업데이트된 회사 정보 재조회
      final updatedCompany = await remoteDataSource.getCompanyDetail(id);
      final company = _mapResponseToDomain(updatedCompany);
      
      return Right(company);
    } catch (e) {
      if (e.toString().contains('404')) {
        return Left(NotFoundFailure(
          message: '상태를 변경할 회사를 찾을 수 없습니다.',
          resourceType: 'Company',
          resourceId: id.toString(),
        ));
      }
      return Left(ServerFailure(
        message: '회사 상태 변경 중 오류가 발생했습니다: ${e.toString()}',
      ));
    }
  }

  @override
  Future<Either<Failure, Branch>> createBranch(int companyId, Branch branch) async {
    try {
      final request = _mapBranchToCreateRequest(branch);
      final result = await remoteDataSource.createBranch(companyId, request);
      final createdBranch = _mapBranchResponseToDomain(result);
      return Right(createdBranch);
    } catch (e) {
      if (e.toString().contains('404')) {
        return Left(NotFoundFailure(
          message: '해당 회사를 찾을 수 없습니다.',
          resourceType: 'Company',
          resourceId: companyId.toString(),
        ));
      }
      return Left(ServerFailure(
        message: '지점 생성 중 오류가 발생했습니다: ${e.toString()}',
      ));
    }
  }

  @override
  Future<Either<Failure, Branch>> updateBranch(int companyId, int branchId, Branch branch) async {
    try {
      final request = _mapBranchToUpdateRequest(branch);
      final result = await remoteDataSource.updateBranch(companyId, branchId, request);
      final updatedBranch = _mapBranchResponseToDomain(result);
      return Right(updatedBranch);
    } catch (e) {
      if (e.toString().contains('404')) {
        return Left(NotFoundFailure(
          message: '수정할 지점을 찾을 수 없습니다.',
          resourceType: 'Branch',
          resourceId: branchId.toString(),
        ));
      }
      return Left(ServerFailure(
        message: '지점 정보 수정 중 오류가 발생했습니다: ${e.toString()}',
      ));
    }
  }

  @override
  Future<Either<Failure, void>> deleteBranch(int companyId, int branchId) async {
    try {
      await remoteDataSource.deleteBranch(companyId, branchId);
      return const Right(null);
    } catch (e) {
      if (e.toString().contains('404')) {
        return Left(NotFoundFailure(
          message: '삭제할 지점을 찾을 수 없습니다.',
          resourceType: 'Branch',
          resourceId: branchId.toString(),
        ));
      }
      return Left(ServerFailure(
        message: '지점 삭제 중 오류가 발생했습니다: ${e.toString()}',
      ));
    }
  }

  @override
  Future<Either<Failure, List<String>>> searchCompanyNames(String query, {int? limit}) async {
    try {
      final companies = await remoteDataSource.searchCompanies(query);
      final names = companies.map((company) => company.name).take(limit ?? 10).toList();
      return Right(names);
    } catch (e) {
      return Left(ServerFailure(
        message: '회사명 검색 중 오류가 발생했습니다: ${e.toString()}',
      ));
    }
  }

  @override
  Future<Either<Failure, Map<CompanyType, int>>> getCompanyCountByType() async {
    // TODO: API에서 회사 유형별 통계 기능이 구현되면 추가
    return const Left(ServerFailure(
      message: '회사 유형별 통계 기능이 아직 구현되지 않았습니다.',
    ));
  }

  @override
  Future<Either<Failure, bool>> hasLinkedUsers(int companyId) async {
    // TODO: 회사에 연결된 사용자 존재 여부 확인 API 구현 필요
    try {
      // 임시로 false 반환 - API 구현 후 수정 필요
      return const Right(false);
    } catch (e) {
      return Left(ServerFailure(
        message: '연결된 사용자 확인 중 오류가 발생했습니다: ${e.toString()}',
      ));
    }
  }

  @override
  Future<Either<Failure, bool>> hasLinkedEquipment(int companyId) async {
    // TODO: 회사에 연결된 장비 존재 여부 확인 API 구현 필요
    try {
      // 임시로 false 반환 - API 구현 후 수정 필요
      return const Right(false);
    } catch (e) {
      return Left(ServerFailure(
        message: '연결된 장비 확인 중 오류가 발생했습니다: ${e.toString()}',
      ));
    }
  }

  @override
  Future<Either<Failure, bool>> isDuplicateCompanyName(String name, {int? excludeId}) async {
    try {
      final isDuplicate = await remoteDataSource.checkDuplicateCompany(name);
      // excludeId가 있는 경우 해당 ID 제외 로직 추가 필요
      return Right(isDuplicate);
    } catch (e) {
      return Left(ServerFailure(
        message: '중복 회사명 확인 중 오류가 발생했습니다: ${e.toString()}',
      ));
    }
  }

  // Private 매퍼 메서드들
  
  Company _mapDtoToDomain(CompanyListDto dto) {
    return Company(
      id: dto.id,
      name: dto.name,
      address: Address.fromFullAddress(dto.address ?? ''),
      contactName: dto.contactName,
      contactPosition: null,  // CompanyListDto에 없음
      contactPhone: dto.contactPhone,
      contactEmail: dto.contactEmail,
      companyTypes: _parseCompanyTypes(dto.companyTypes),
      remark: null,  // CompanyListDto에 없음
      branches: [], // 목록에서는 지점 정보 비어있음
    );
  }
  
  Company _mapDetailDtoToDomain(CompanyWithChildren dto) {
    return Company(
      id: dto.company.id,
      name: dto.company.name,
      address: Address.fromFullAddress(dto.company.address ?? ''),
      contactName: dto.company.contactName,
      contactPosition: null, // 백엔드에서 미지원
      contactPhone: dto.company.contactPhone,
      contactEmail: dto.company.contactEmail,
      companyTypes: [], // 백엔드에서 미지원
      remark: dto.company.remark,
      branches: [], // TODO: 계층형 구조로 변경됨. children은 자회사를 의미하므로 branches는 빈 리스트로 설정
    );
  }
  
  Company _mapResponseToDomain(CompanyDto response) {
    return Company(
      id: response.id,
      name: response.name,
      address: Address.fromFullAddress(response.address ?? ''),
      contactName: response.contactName,
      contactPosition: null, // 백엔드에서 미지원
      contactPhone: response.contactPhone,
      contactEmail: response.contactEmail,
      companyTypes: [], // 백엔드에서 미지원
      remark: response.remark,
      branches: [], // CompanyDto에서는 지점 정보 따로 조회
    );
  }
  
  Branch _mapBranchResponseToDomain(BranchResponse response) {
    return Branch(
      id: response.id,
      companyId: response.companyId,
      name: response.branchName,
      address: Address.fromFullAddress(response.address ?? ''),
      contactName: response.managerName,
      contactPosition: null,
      contactPhone: response.phone,
      contactEmail: null,
      remark: response.remark,
    );
  }
  
  /// API에서 받은 문자열 리스트를 CompanyType enum 리스트로 변환
  /// 지원하는 형식: ['customer', 'partner'] 또는 ['고객사', '파트너사']
  List<CompanyType> _parseCompanyTypes(List<String>? types) {
    if (types == null || types.isEmpty) return [CompanyType.customer];
    
    return types.map((type) {
      final lowerType = type.toLowerCase().trim();
      // API 문자열 형식 매칭
      if (lowerType == 'partner' || lowerType.contains('partner') || lowerType == '파트너사') {
        return CompanyType.partner;
      }
      // 기본값은 customer
      return CompanyType.customer;
    }).toList();
  }
  
  
  CompanyRequestDto _mapDomainToCreateRequest(Company company) {
    return CompanyRequestDto(
      name: company.name,
      address: company.address.toString(),
      contactName: company.contactName ?? '',
      contactPhone: company.contactPhone ?? '',
      contactEmail: company.contactEmail ?? '',
      remark: company.remark,
    );
  }
  
  CompanyUpdateRequestDto _mapDomainToUpdateRequest(Company company) {
    return CompanyUpdateRequestDto(
      name: company.name,
      address: company.address.toString(),
      contactName: company.contactName,
      contactPhone: company.contactPhone,
      contactEmail: company.contactEmail,
      remark: company.remark,
      isActive: null,  // CompanyUpdateRequestDto에서 필요한 경우 추가
    );
  }
  
  CreateBranchRequest _mapBranchToCreateRequest(Branch branch) {
    return CreateBranchRequest(
      branchName: branch.name,
      address: branch.address.toString(),
      phone: branch.contactPhone ?? '',
      managerName: branch.contactName,
      managerPhone: null,  // Branch에 없음
      remark: branch.remark,
    );
  }
  
  UpdateBranchRequest _mapBranchToUpdateRequest(Branch branch) {
    return UpdateBranchRequest(
      branchName: branch.name,
      address: branch.address.toString(),
      phone: branch.contactPhone,
      managerName: branch.contactName,
      managerPhone: null,  // Branch에 없음
      remark: branch.remark,
    );
  }

  // 계층 구조 관련 메서드 구현

  @override
  Future<Either<Failure, List<Company>>> getCompanyHierarchy({
    bool includeInactive = false,
  }) async {
    try {
      // 모든 회사 조회
      final result = await remoteDataSource.getCompanies(
        page: 1,
        perPage: 1000, // 전체 회사 조회를 위한 큰 수
        isActive: includeInactive ? null : true,
      );
      
      // DTO를 도메인 모델로 변환
      final companies = result.items.map((dto) => _mapDtoToDomain(dto)).toList();
      
      // 계층 구조로 재구성 (클라이언트에서 처리)
      // 실제 API가 계층 구조를 반환하면 이 부분 수정 필요
      
      return Right(companies);
    } catch (e) {
      return Left(ServerFailure(
        message: '회사 계층 구조 조회 중 오류가 발생했습니다: ${e.toString()}',
      ));
    }
  }

  @override
  Future<Either<Failure, List<Company>>> getChildrenCompanies(
    int companyId, {
    bool recursive = false,
  }) async {
    try {
      // API에서 자식 회사 조회
      // 현재는 getCompanyWithChildren 사용
      final result = await remoteDataSource.getCompanyWithChildren(companyId);
      
      final children = <Company>[];
      for (final childDto in result.children) {
          final child = _mapResponseToDomain(childDto);
          children.add(child);
          
          // 재귀적으로 모든 자손 포함
          if (recursive && childDto.id != null && childDto.id != companyId) {
            final grandChildrenResult = await getChildrenCompanies(childDto.id!, recursive: true);
            grandChildrenResult.fold(
              (failure) {}, // 에러 무시하고 진행
              (grandChildren) => children.addAll(grandChildren),
            );
          }
        }
      
      return Right(children);
    } catch (e) {
      return Left(ServerFailure(
        message: '자식 회사 조회 중 오류가 발생했습니다: ${e.toString()}',
      ));
    }
  }

  @override
  Future<Either<Failure, List<Company>>> getAncestorPath(int companyId) async {
    try {
      final path = <Company>[];
      int? currentId = companyId;
      
      while (currentId != null) {
        final companyResult = await getCompanyById(currentId);
        
        final company = companyResult.fold(
          (failure) => null,
          (company) => company,
        );
        
        if (company == null) break;
        
        path.insert(0, company); // 루트부터 시작하도록 앞에 삽입
        
        // parent_company_id를 찾기 위해 API 호출 필요
        // 현재 CompanyDto에 parentCompanyId가 있으므로 사용
        final detailResult = await remoteDataSource.getCompanyWithChildren(currentId);
        currentId = detailResult.company.parentCompanyId;
      }
      
      return Right(path);
    } catch (e) {
      return Left(ServerFailure(
        message: '부모 경로 조회 중 오류가 발생했습니다: ${e.toString()}',
      ));
    }
  }

  @override
  Future<Either<Failure, Company>> updateParentCompany(
    int companyId,
    int? newParentId,
  ) async {
    try {
      // 먼저 현재 회사 정보 조회
      final currentResult = await getCompanyById(companyId);
      
      return currentResult.fold(
        (failure) => Left(failure),
        (company) async {
          // 부모 회사 ID만 업데이트
          final updateRequest = CompanyUpdateRequestDto(
            parentCompanyId: newParentId,
          );
          
          final result = await remoteDataSource.updateCompany(companyId, updateRequest);
          final updatedCompany = _mapResponseToDomain(result);
          return Right(updatedCompany);
        },
      );
    } catch (e) {
      return Left(ServerFailure(
        message: '부모 회사 변경 중 오류가 발생했습니다: ${e.toString()}',
      ));
    }
  }

  @override
  Future<Either<Failure, bool>> hasChildrenCompanies(int companyId) async {
    try {
      final childrenResult = await getChildrenCompanies(companyId, recursive: false);
      
      return childrenResult.fold(
        (failure) => Left(failure),
        (children) => Right(children.isNotEmpty),
      );
    } catch (e) {
      return Left(ServerFailure(
        message: '자식 회사 존재 여부 확인 중 오류가 발생했습니다: ${e.toString()}',
      ));
    }
  }

  @override
  Future<Either<Failure, bool>> validateHierarchyChange(
    int companyId,
    int? newParentId,
  ) async {
    try {
      if (newParentId == null) {
        // 루트로 변경하는 경우는 항상 유효
        return const Right(true);
      }
      
      // 자기 자신을 부모로 설정하려는 경우
      if (companyId == newParentId) {
        return const Right(false);
      }
      
      // 자손을 부모로 설정하려는 경우 검증
      final descendantsResult = await getChildrenCompanies(companyId, recursive: true);
      
      return descendantsResult.fold(
        (failure) => Left(failure),
        (descendants) {
          final descendantIds = descendants.map((c) => c.id).toList();
          if (descendantIds.contains(newParentId)) {
            return const Right(false);
          }
          return const Right(true);
        },
      );
    } catch (e) {
      return Left(ServerFailure(
        message: '계층 구조 유효성 검증 중 오류가 발생했습니다: ${e.toString()}',
      ));
    }
  }
}
