import { Repository, IsNull } from 'typeorm';
import { AppDataSource } from '../../config/typeorm.js';
import { Partner } from './entities/index.js';
import { NotFoundError } from '../../shared/types/index.js';
import { logger } from '../../shared/utils/logger.js';

// ============================================================================
// TYPES
// ============================================================================

export type ABCClassification = 'A' | 'B' | 'C' | null;

export interface PartnerRanking {
  id: string;
  tenant_id: string;
  partner_id: string;
  partner_name?: string;
  company_id: string | null;
  period_start: Date;
  period_end: Date;
  total_sales: number;
  sales_order_count: number;
  avg_order_value: number;
  total_purchases: number;
  purchase_order_count: number;
  avg_purchase_value: number;
  avg_payment_days: number | null;
  on_time_payment_rate: number | null;
  sales_rank: number | null;
  purchase_rank: number | null;
  customer_abc: ABCClassification;
  supplier_abc: ABCClassification;
  customer_score: number | null;
  supplier_score: number | null;
  overall_score: number | null;
  sales_trend: number | null;
  purchase_trend: number | null;
  calculated_at: Date;
}

export interface RankingCalculationResult {
  partners_processed: number;
  customers_ranked: number;
  suppliers_ranked: number;
}

export interface RankingFilters {
  company_id?: string;
  period_start?: string;
  period_end?: string;
  customer_abc?: ABCClassification;
  supplier_abc?: ABCClassification;
  min_sales?: number;
  min_purchases?: number;
  page?: number;
  limit?: number;
}

export interface TopPartner {
  id: string;
  tenant_id: string;
  name: string;
  email: string | null;
  is_customer: boolean;
  is_supplier: boolean;
  customer_rank: number | null;
  supplier_rank: number | null;
  customer_abc: ABCClassification;
  supplier_abc: ABCClassification;
  total_sales_ytd: number;
  total_purchases_ytd: number;
  last_ranking_date: Date | null;
  customer_category: string | null;
  supplier_category: string | null;
}

// ============================================================================
// SERVICE
// ============================================================================

class RankingService {
  private partnerRepository: Repository<Partner>;

  constructor() {
    this.partnerRepository = AppDataSource.getRepository(Partner);
  }

  /**
   * Calculate rankings for all partners in a tenant
   * Uses the database function for atomic calculation
   */
  async calculateRankings(
    tenantId: string,
    companyId?: string,
    periodStart?: string,
    periodEnd?: string
  ): Promise<RankingCalculationResult> {
    try {
      const result = await this.partnerRepository.query(
        `SELECT * FROM core.calculate_partner_rankings($1, $2, $3, $4)`,
        [tenantId, companyId || null, periodStart || null, periodEnd || null]
      );

      const data = result[0];
      if (!data) {
        throw new Error('Error calculando rankings');
      }

      logger.info('Partner rankings calculated', {
        tenantId,
        companyId,
        periodStart,
        periodEnd,
        result: data,
      });

      return {
        partners_processed: parseInt(data.partners_processed, 10),
        customers_ranked: parseInt(data.customers_ranked, 10),
        suppliers_ranked: parseInt(data.suppliers_ranked, 10),
      };
    } catch (error) {
      logger.error('Error calculating partner rankings', {
        error: (error as Error).message,
        tenantId,
        companyId,
      });
      throw error;
    }
  }

  /**
   * Get rankings for a specific period
   */
  async findRankings(
    tenantId: string,
    filters: RankingFilters = {}
  ): Promise<{ data: PartnerRanking[]; total: number }> {
    try {
      const {
        company_id,
        period_start,
        period_end,
        customer_abc,
        supplier_abc,
        min_sales,
        min_purchases,
        page = 1,
        limit = 20,
      } = filters;

      const conditions: string[] = ['pr.tenant_id = $1'];
      const params: any[] = [tenantId];
      let idx = 2;

      if (company_id) {
        conditions.push(`pr.company_id = $${idx++}`);
        params.push(company_id);
      }

      if (period_start) {
        conditions.push(`pr.period_start >= $${idx++}`);
        params.push(period_start);
      }

      if (period_end) {
        conditions.push(`pr.period_end <= $${idx++}`);
        params.push(period_end);
      }

      if (customer_abc) {
        conditions.push(`pr.customer_abc = $${idx++}`);
        params.push(customer_abc);
      }

      if (supplier_abc) {
        conditions.push(`pr.supplier_abc = $${idx++}`);
        params.push(supplier_abc);
      }

      if (min_sales !== undefined) {
        conditions.push(`pr.total_sales >= $${idx++}`);
        params.push(min_sales);
      }

      if (min_purchases !== undefined) {
        conditions.push(`pr.total_purchases >= $${idx++}`);
        params.push(min_purchases);
      }

      const whereClause = conditions.join(' AND ');

      // Count total
      const countResult = await this.partnerRepository.query(
        `SELECT COUNT(*) as count FROM core.partner_rankings pr WHERE ${whereClause}`,
        params
      );

      // Get data with pagination
      const offset = (page - 1) * limit;
      params.push(limit, offset);

      const data = await this.partnerRepository.query(
        `SELECT pr.*,
                p.name as partner_name
         FROM core.partner_rankings pr
         JOIN core.partners p ON pr.partner_id = p.id
         WHERE ${whereClause}
         ORDER BY pr.overall_score DESC NULLS LAST, pr.total_sales DESC
         LIMIT $${idx} OFFSET $${idx + 1}`,
        params
      );

      return {
        data,
        total: parseInt(countResult[0]?.count || '0', 10),
      };
    } catch (error) {
      logger.error('Error retrieving partner rankings', {
        error: (error as Error).message,
        tenantId,
      });
      throw error;
    }
  }

  /**
   * Get ranking for a specific partner
   */
  async findPartnerRanking(
    partnerId: string,
    tenantId: string,
    periodStart?: string,
    periodEnd?: string
  ): Promise<PartnerRanking | null> {
    try {
      let sql = `
        SELECT pr.*, p.name as partner_name
        FROM core.partner_rankings pr
        JOIN core.partners p ON pr.partner_id = p.id
        WHERE pr.partner_id = $1 AND pr.tenant_id = $2
      `;
      const params: any[] = [partnerId, tenantId];

      if (periodStart && periodEnd) {
        sql += ` AND pr.period_start = $3 AND pr.period_end = $4`;
        params.push(periodStart, periodEnd);
      } else {
        // Get most recent ranking
        sql += ` ORDER BY pr.calculated_at DESC LIMIT 1`;
      }

      const result = await this.partnerRepository.query(sql, params);
      return result[0] || null;
    } catch (error) {
      logger.error('Error finding partner ranking', {
        error: (error as Error).message,
        partnerId,
        tenantId,
      });
      throw error;
    }
  }

  /**
   * Get top partners (customers or suppliers)
   */
  async getTopPartners(
    tenantId: string,
    type: 'customers' | 'suppliers',
    limit: number = 10
  ): Promise<TopPartner[]> {
    try {
      const orderColumn = type === 'customers' ? 'customer_rank' : 'supplier_rank';

      const result = await this.partnerRepository.query(
        `SELECT * FROM core.top_partners_view
         WHERE tenant_id = $1 AND ${orderColumn} IS NOT NULL
         ORDER BY ${orderColumn} ASC
         LIMIT $2`,
        [tenantId, limit]
      );

      return result;
    } catch (error) {
      logger.error('Error getting top partners', {
        error: (error as Error).message,
        tenantId,
        type,
      });
      throw error;
    }
  }

  /**
   * Get ABC distribution summary
   */
  async getABCDistribution(
    tenantId: string,
    type: 'customers' | 'suppliers',
    companyId?: string
  ): Promise<{
    A: { count: number; total_value: number; percentage: number };
    B: { count: number; total_value: number; percentage: number };
    C: { count: number; total_value: number; percentage: number };
  }> {
    try {
      const abcColumn = type === 'customers' ? 'customer_abc' : 'supplier_abc';
      const valueColumn = type === 'customers' ? 'total_sales_ytd' : 'total_purchases_ytd';

      const whereClause = `tenant_id = $1 AND ${abcColumn} IS NOT NULL`;
      const params: any[] = [tenantId];

      const result = await this.partnerRepository.query(
        `SELECT
           ${abcColumn} as abc,
           COUNT(*) as count,
           COALESCE(SUM(${valueColumn}), 0) as total_value
         FROM core.partners
         WHERE ${whereClause} AND deleted_at IS NULL
         GROUP BY ${abcColumn}
         ORDER BY ${abcColumn}`,
        params
      );

      // Calculate totals
      const grandTotal = result.reduce((sum: number, r: any) => sum + parseFloat(r.total_value), 0);

      const distribution = {
        A: { count: 0, total_value: 0, percentage: 0 },
        B: { count: 0, total_value: 0, percentage: 0 },
        C: { count: 0, total_value: 0, percentage: 0 },
      };

      for (const row of result) {
        const abc = row.abc as 'A' | 'B' | 'C';
        if (abc in distribution) {
          distribution[abc] = {
            count: parseInt(row.count, 10),
            total_value: parseFloat(row.total_value),
            percentage: grandTotal > 0 ? (parseFloat(row.total_value) / grandTotal) * 100 : 0,
          };
        }
      }

      return distribution;
    } catch (error) {
      logger.error('Error getting ABC distribution', {
        error: (error as Error).message,
        tenantId,
        type,
      });
      throw error;
    }
  }

  /**
   * Get ranking history for a partner
   */
  async getPartnerRankingHistory(
    partnerId: string,
    tenantId: string,
    limit: number = 12
  ): Promise<PartnerRanking[]> {
    try {
      const result = await this.partnerRepository.query(
        `SELECT pr.*, p.name as partner_name
         FROM core.partner_rankings pr
         JOIN core.partners p ON pr.partner_id = p.id
         WHERE pr.partner_id = $1 AND pr.tenant_id = $2
         ORDER BY pr.period_end DESC
         LIMIT $3`,
        [partnerId, tenantId, limit]
      );

      return result;
    } catch (error) {
      logger.error('Error getting partner ranking history', {
        error: (error as Error).message,
        partnerId,
        tenantId,
      });
      throw error;
    }
  }

  /**
   * Get partners by ABC classification
   */
  async findPartnersByABC(
    tenantId: string,
    abc: ABCClassification,
    type: 'customers' | 'suppliers',
    page: number = 1,
    limit: number = 20
  ): Promise<{ data: TopPartner[]; total: number }> {
    try {
      const abcColumn = type === 'customers' ? 'customer_abc' : 'supplier_abc';
      const offset = (page - 1) * limit;

      const countResult = await this.partnerRepository.query(
        `SELECT COUNT(*) as count FROM core.partners
         WHERE tenant_id = $1 AND ${abcColumn} = $2 AND deleted_at IS NULL`,
        [tenantId, abc]
      );

      const data = await this.partnerRepository.query(
        `SELECT * FROM core.top_partners_view
         WHERE tenant_id = $1 AND ${abcColumn} = $2
         ORDER BY ${type === 'customers' ? 'total_sales_ytd' : 'total_purchases_ytd'} DESC
         LIMIT $3 OFFSET $4`,
        [tenantId, abc, limit, offset]
      );

      return {
        data,
        total: parseInt(countResult[0]?.count || '0', 10),
      };
    } catch (error) {
      logger.error('Error finding partners by ABC', {
        error: (error as Error).message,
        tenantId,
        abc,
        type,
      });
      throw error;
    }
  }
}

export const rankingService = new RankingService();
