import { Inject } from '@nestjs/common';
import { type IQueryHandler, QueryHandler } from '@nestjs/cqrs';
import { PrismaService } from '@modules/shared';
import { AnalyzeIndustrialLocationQuery } from './analyze-industrial-location.query';

interface ConnectivityInfo {
  nearest_port?: { name: string; distanceKm: number };
  nearest_airport?: { name: string; distanceKm: number };
  nearest_highway?: { name: string; distanceKm: number };
  nearest_railway?: { name: string; distanceKm: number };
}

interface InfrastructureInfo {
  power_availability?: string;
  water_supply?: string;
  wastewater_treatment?: string;
  telecom?: string;
}

interface LocationAnalysisResult {
  overall_score: number;
  connectivity: ConnectivityInfo;
  infrastructure: InfrastructureInfo;
  labor_market: {
    worker_pool_radius_30km: number | null;
    average_wage_usd: number | null;
    nearby_universities: string[];
  };
  incentives: string[];
  risks: string[];
  nearby_parks: { name: string; distanceKm: number; occupancyRate: number }[];
}

@QueryHandler(AnalyzeIndustrialLocationQuery)
export class AnalyzeIndustrialLocationHandler
  implements IQueryHandler<AnalyzeIndustrialLocationQuery, LocationAnalysisResult>
{
  constructor(@Inject(PrismaService) private readonly prisma: PrismaService) {}

  async execute(query: AnalyzeIndustrialLocationQuery): Promise<LocationAnalysisResult> {
    const { latitude, longitude, parkName, targetIndustry } = query;

    // Find nearest parks within 50km using PostGIS
    const nearbyParks = await this.prisma.$queryRaw<
      Array<{
        id: string;
        name: string;
        province: string;
        region: string;
        distanceKm: number;
        occupancyRate: number;
        landRentUsdM2Year: number | null;
        infrastructure: Record<string, unknown> | null;
        connectivity: Record<string, unknown> | null;
        incentives: Record<string, unknown> | null;
        targetIndustries: string[];
      }>
    >`
      SELECT
        id, name, province, region,
        "occupancyRate",
        "landRentUsdM2Year",
        infrastructure::jsonb as infrastructure,
        connectivity::jsonb as connectivity,
        incentives::jsonb as incentives,
        "targetIndustries",
        ST_Distance(
          location::geography,
          ST_SetSRID(ST_MakePoint(${longitude}, ${latitude}), 4326)::geography
        ) / 1000.0 AS "distanceKm"
      FROM "IndustrialPark"
      WHERE ST_DWithin(
        location::geography,
        ST_SetSRID(ST_MakePoint(${longitude}, ${latitude}), 4326)::geography,
        50000
      )
      ORDER BY "distanceKm" ASC
      LIMIT 10
    `;

    // If parkName specified, find that specific park
    let targetPark = nearbyParks[0] ?? null;
    if (parkName) {
      const matched = nearbyParks.find(
        (p) => p.name.toLowerCase().includes(parkName.toLowerCase()),
      );
      if (matched) targetPark = matched;
    }

    // Build connectivity from nearest park data
    const connectivity = this.buildConnectivity(targetPark?.connectivity);

    // Build infrastructure from nearest park data
    const infrastructure = this.buildInfrastructure(targetPark?.infrastructure);

    // Compute labor market estimates based on province/region
    const laborMarket = this.estimateLaborMarket(targetPark?.province ?? null, targetPark?.region ?? null);

    // Gather incentives
    const incentives = this.gatherIncentives(targetPark?.incentives);

    // Assess risks
    const risks = this.assessRisks(nearbyParks, targetIndustry);

    // Calculate overall score (0-100)
    const overallScore = this.calculateScore(
      connectivity,
      infrastructure,
      nearbyParks,
      targetIndustry,
      targetPark,
    );

    return {
      overall_score: overallScore,
      connectivity,
      infrastructure,
      labor_market: laborMarket,
      incentives,
      risks,
      nearby_parks: nearbyParks.slice(0, 5).map((p) => ({
        name: p.name,
        distanceKm: Math.round(p.distanceKm * 10) / 10,
        occupancyRate: p.occupancyRate,
      })),
    };
  }

  private buildConnectivity(raw: Record<string, unknown> | null | undefined): ConnectivityInfo {
    if (!raw) return {};
    return {
      nearest_port: this.extractFacility(raw, 'nearestPort', 'seaport'),
      nearest_airport: this.extractFacility(raw, 'airport', 'nearestAirport'),
      nearest_highway: this.extractFacility(raw, 'highway', 'nearestHighway'),
      nearest_railway: this.extractFacility(raw, 'railway', 'nearestRailway'),
    };
  }

  private extractFacility(
    raw: Record<string, unknown>,
    ...keys: string[]
  ): { name: string; distanceKm: number } | undefined {
    for (const key of keys) {
      const val = raw[key] as Record<string, unknown> | string | undefined;
      if (val && typeof val === 'object' && 'name' in val) {
        return { name: String(val['name']), distanceKm: Number(val['distanceKm'] ?? val['distance'] ?? 0) };
      }
      if (typeof val === 'string') {
        return { name: val, distanceKm: 0 };
      }
    }
    return undefined;
  }

  private buildInfrastructure(raw: Record<string, unknown> | null | undefined): InfrastructureInfo {
    if (!raw) return {};
    return {
      power_availability: raw['electricity'] ? String(raw['electricity']) : undefined,
      water_supply: raw['water'] ? String(raw['water']) : undefined,
      wastewater_treatment: raw['wastewater'] ? String(raw['wastewater']) : undefined,
      telecom: raw['telecom'] ? String(raw['telecom']) : undefined,
    };
  }

  private estimateLaborMarket(province: string | null, region: string | null) {
    // Regional labor market estimates for Vietnam industrial zones
    const regionData: Record<string, { workers: number; wage: number; unis: string[] }> = {
      SOUTH: {
        workers: 500_000,
        wage: 350,
        unis: ['ĐH Bách Khoa TP.HCM', 'ĐH Công nghiệp TP.HCM', 'ĐH Tôn Đức Thắng'],
      },
      NORTH: {
        workers: 400_000,
        wage: 300,
        unis: ['ĐH Bách Khoa Hà Nội', 'ĐH Công nghiệp Hà Nội'],
      },
      CENTRAL: {
        workers: 200_000,
        wage: 280,
        unis: ['ĐH Bách Khoa Đà Nẵng', 'ĐH Kinh tế Đà Nẵng'],
      },
    };

    const data = regionData[region ?? 'SOUTH'] ?? regionData['SOUTH']!;
    return {
      worker_pool_radius_30km: data!.workers,
      average_wage_usd: data!.wage,
      nearby_universities: data!.unis,
    };
  }

  private gatherIncentives(raw: Record<string, unknown> | null | undefined): string[] {
    if (!raw) return [];
    const result: string[] = [];
    if (raw['taxHoliday']) result.push(`Tax holiday: ${raw['taxHoliday']}`);
    if (raw['importDuty']) result.push(`Import duty exemption: ${raw['importDuty']}`);
    if (raw['landRentReduction']) result.push(`Land rent reduction: ${raw['landRentReduction']}`);
    if (raw['specialZone']) result.push(`Special economic zone: ${raw['specialZone']}`);
    return result;
  }

  private assessRisks(
    nearbyParks: Array<{ occupancyRate: number; province: string }>,
    targetIndustry?: string | null,
  ): string[] {
    const risks: string[] = [];

    if (nearbyParks.length === 0) {
      risks.push('No industrial parks within 50km — limited industrial ecosystem');
    }

    const avgOccupancy =
      nearbyParks.length > 0
        ? nearbyParks.reduce((sum, p) => sum + p.occupancyRate, 0) / nearbyParks.length
        : 0;

    if (avgOccupancy > 90) {
      risks.push('High area occupancy (>90%) — limited expansion options');
    }

    if (targetIndustry) {
      // Check if any nearby park targets this industry — simplified check
      const hasMatchingPark = nearbyParks.some(
        (p) => (p as unknown as { targetIndustries?: string[] }).targetIndustries?.some(
          (t) => t.toLowerCase().includes(targetIndustry.toLowerCase()),
        ),
      );
      if (!hasMatchingPark) {
        risks.push(`No nearby parks specialize in "${targetIndustry}" — may lack ecosystem support`);
      }
    }

    return risks;
  }

  private calculateScore(
    connectivity: ConnectivityInfo,
    infrastructure: InfrastructureInfo,
    nearbyParks: Array<{ occupancyRate: number; distanceKm: number }>,
    targetIndustry?: string | null,
    targetPark?: { targetIndustries?: string[]; occupancyRate?: number } | null,
  ): number {
    let score = 50; // Base score

    // Connectivity bonus (up to +20)
    let connectivityPoints = 0;
    if (connectivity.nearest_port) connectivityPoints += 5;
    if (connectivity.nearest_airport) connectivityPoints += 5;
    if (connectivity.nearest_highway) connectivityPoints += 5;
    if (connectivity.nearest_railway) connectivityPoints += 5;
    score += connectivityPoints;

    // Infrastructure bonus (up to +15)
    let infraPoints = 0;
    if (infrastructure.power_availability) infraPoints += 4;
    if (infrastructure.water_supply) infraPoints += 4;
    if (infrastructure.wastewater_treatment) infraPoints += 4;
    if (infrastructure.telecom) infraPoints += 3;
    score += infraPoints;

    // Nearby parks density (up to +10)
    if (nearbyParks.length >= 5) score += 10;
    else if (nearbyParks.length >= 3) score += 7;
    else if (nearbyParks.length >= 1) score += 4;

    // Occupancy rate penalty (parks too full = -5)
    if (targetPark && targetPark.occupancyRate && targetPark.occupancyRate > 95) {
      score -= 5;
    }

    // Industry match bonus (+5)
    if (targetIndustry && targetPark?.targetIndustries?.some(
      (t) => t.toLowerCase().includes(targetIndustry.toLowerCase()),
    )) {
      score += 5;
    }

    return Math.max(0, Math.min(100, Math.round(score)));
  }
}
