/**
 * GeographyService - Country and State Management
 *
 * Provides centralized geography operations:
 * - Country management
 * - State/Province management
 * - Timezone utilities
 *
 * @module Core
 */

import { Repository } from 'typeorm';
import { Country } from '../entities/country.entity';
import { State } from '../entities/state.entity';

/**
 * Service context for multi-tenant operations
 */
export interface ServiceContext {
  tenantId: string;
  userId?: string;
}

/**
 * Paginated result interface
 */
export interface PaginatedResult<T> {
  data: T[];
  total: number;
  page: number;
  limit: number;
  totalPages: number;
}

export interface StateFilters {
  countryId?: string;
  countryCode?: string;
  isActive?: boolean;
  search?: string;
}

export class GeographyService {
  private countryRepository: Repository<Country>;
  private stateRepository: Repository<State>;

  constructor(
    countryRepository: Repository<Country>,
    stateRepository: Repository<State>
  ) {
    this.countryRepository = countryRepository;
    this.stateRepository = stateRepository;
  }

  // ==================== COUNTRY OPERATIONS ====================

  /**
   * Get all countries
   */
  async findAllCountries(): Promise<Country[]> {
    return this.countryRepository.find({
      order: { name: 'ASC' },
    });
  }

  /**
   * Find country by ID
   */
  async findCountryById(id: string): Promise<Country | null> {
    return this.countryRepository.findOne({
      where: { id },
    });
  }

  /**
   * Find country by code (ISO 2-letter code)
   */
  async findCountryByCode(code: string): Promise<Country | null> {
    return this.countryRepository.findOne({
      where: { code: code.toUpperCase() },
    });
  }

  /**
   * Get default country (Mexico for this ERP)
   */
  async getDefaultCountry(): Promise<Country | null> {
    const mexico = await this.findCountryByCode('MX');
    if (mexico) return mexico;

    // Fallback to first country
    const countries = await this.findAllCountries();
    return countries.length > 0 ? countries[0] : null;
  }

  /**
   * Search countries by name
   */
  async searchCountries(query: string): Promise<Country[]> {
    return this.countryRepository
      .createQueryBuilder('country')
      .where('country.name ILIKE :query', { query: `%${query}%` })
      .orWhere('country.code ILIKE :query', { query: `%${query}%` })
      .orderBy('country.name', 'ASC')
      .take(20)
      .getMany();
  }

  // ==================== STATE OPERATIONS ====================

  /**
   * Get all states for a country
   */
  async findStatesByCountry(countryId: string): Promise<State[]> {
    return this.stateRepository.find({
      where: { countryId, isActive: true },
      order: { name: 'ASC' },
    });
  }

  /**
   * Get all states for a country by code
   */
  async findStatesByCountryCode(countryCode: string): Promise<State[]> {
    const country = await this.findCountryByCode(countryCode);
    if (!country) {
      return [];
    }
    return this.findStatesByCountry(country.id);
  }

  /**
   * Find state by ID
   */
  async findStateById(id: string): Promise<State | null> {
    return this.stateRepository.findOne({
      where: { id },
      relations: ['country'],
    });
  }

  /**
   * Find state by code within a country
   */
  async findStateByCode(
    countryId: string,
    stateCode: string
  ): Promise<State | null> {
    return this.stateRepository.findOne({
      where: { countryId, code: stateCode.toUpperCase() },
      relations: ['country'],
    });
  }

  /**
   * Find states with filters and pagination
   */
  async findStatesWithFilters(
    filters: StateFilters,
    page = 1,
    limit = 50
  ): Promise<PaginatedResult<State>> {
    const qb = this.stateRepository
      .createQueryBuilder('state')
      .leftJoinAndSelect('state.country', 'country');

    if (filters.countryId) {
      qb.andWhere('state.country_id = :countryId', {
        countryId: filters.countryId,
      });
    }
    if (filters.countryCode) {
      qb.andWhere('country.code = :countryCode', {
        countryCode: filters.countryCode.toUpperCase(),
      });
    }
    if (filters.isActive !== undefined) {
      qb.andWhere('state.is_active = :isActive', { isActive: filters.isActive });
    }
    if (filters.search) {
      qb.andWhere('(state.name ILIKE :search OR state.code ILIKE :search)', {
        search: `%${filters.search}%`,
      });
    }

    const skip = (page - 1) * limit;
    qb.orderBy('state.name', 'ASC').skip(skip).take(limit);

    const [data, total] = await qb.getManyAndCount();

    return {
      data,
      total,
      page,
      limit,
      totalPages: Math.ceil(total / limit),
    };
  }

  /**
   * Search states by name
   */
  async searchStates(query: string, countryId?: string): Promise<State[]> {
    const qb = this.stateRepository
      .createQueryBuilder('state')
      .leftJoinAndSelect('state.country', 'country')
      .where('state.is_active = :isActive', { isActive: true })
      .andWhere('(state.name ILIKE :query OR state.code ILIKE :query)', {
        query: `%${query}%`,
      });

    if (countryId) {
      qb.andWhere('state.country_id = :countryId', { countryId });
    }

    return qb.orderBy('state.name', 'ASC').take(20).getMany();
  }

  /**
   * Get timezone for a state
   */
  async getTimezone(stateId: string): Promise<string | null> {
    const state = await this.findStateById(stateId);
    return state?.timezone || null;
  }

  /**
   * Get default timezone for Mexico
   */
  getDefaultTimezone(): string {
    return 'America/Mexico_City';
  }

  // ==================== UTILITY OPERATIONS ====================

  /**
   * Get full location string (City, State, Country)
   */
  async formatLocation(
    city: string | null,
    stateId: string | null,
    countryId: string | null
  ): Promise<string> {
    const parts: string[] = [];

    if (city) {
      parts.push(city);
    }

    if (stateId) {
      const state = await this.findStateById(stateId);
      if (state) {
        parts.push(state.name);
      }
    }

    if (countryId) {
      const country = await this.findCountryById(countryId);
      if (country) {
        parts.push(country.name);
      }
    }

    return parts.join(', ');
  }

  /**
   * Validate country and state combination
   */
  async validateLocation(
    countryId: string,
    stateId: string
  ): Promise<{ valid: boolean; error?: string }> {
    const country = await this.findCountryById(countryId);
    if (!country) {
      return { valid: false, error: 'Country not found' };
    }

    const state = await this.findStateById(stateId);
    if (!state) {
      return { valid: false, error: 'State not found' };
    }

    if (state.countryId !== countryId) {
      return { valid: false, error: 'State does not belong to the specified country' };
    }

    if (!state.isActive) {
      return { valid: false, error: 'State is not active' };
    }

    return { valid: true };
  }

  /**
   * Get Mexican states (commonly used)
   */
  async getMexicanStates(): Promise<State[]> {
    return this.findStatesByCountryCode('MX');
  }

  /**
   * Get phone code for a country
   */
  async getPhoneCode(countryId: string): Promise<string | null> {
    const country = await this.findCountryById(countryId);
    return country?.phoneCode || null;
  }
}
