import { HttpStatus, Inject } from '@nestjs/common';
import { QueryBus, QueryHandler, type IQueryHandler } from '@nestjs/cqrs';
import {
  AI_CONFIG_PROVIDER,
  DomainException,
  ErrorCode,
  type IAIConfigProvider,
  LoggerService,
} from '@modules/shared';
import {
  PROJECT_REPOSITORY,
  type IProjectRepository,
  type ProjectDetailData,
} from '@modules/projects';
import { type AnthropicUsage } from '../_shared/ai-json-client';
import {
  asString,
  asStringArray,
  callAnthropicJson,
  isRecord,
  jsonShapeError,
  parseJsonObject,
} from '../_shared/ai-json-client';
import {
  type NearbyPOIDto,
  type NearbyPOIsResultDto,
} from '../get-nearby-pois/get-nearby-pois.handler';
import { GetNearbyPOIsQuery } from '../get-nearby-pois/get-nearby-pois.query';
import { type NeighborhoodScoreResult } from '../../../domain/services/neighborhood-score.service';
import { GetNeighborhoodScoreQuery } from '../get-neighborhood-score/get-neighborhood-score.query';
import { GetProjectAiAdviceQuery } from './get-project-ai-advice.query';

/**
 * AI advisor for a Residential Project. Mirrors `get-listing-ai-advice` but
 * keys off project signals (developer reputation, amenity mix, status,
 * completion timing, price range) and omits the `valuation` block — a project
 * is a price range, not a single unit.
 */

export interface ProjectAiAdvice {
  summary: string;
  pros: string[];
  cons: string[];
  suitableFor: string[];
}

export interface ProjectAiAdviceResponse {
  advice: ProjectAiAdvice;
  model: string;
  cacheHit: boolean;
  cacheUsage?: AnthropicUsage;
}

const SYSTEM_PROMPT = `Bạn là chuyên gia phân tích dự án bất động sản Việt Nam (HCM & Hà Nội). Dựa trên dữ liệu về dự án, chủ đầu tư, tiện ích và khu vực, hãy đưa ra nhận định ngắn gọn về dự án.

Bối cảnh thị trường Việt Nam:
- Dự án sơ cấp (primary market) thường mở bán theo giai đoạn, giá tăng dần theo tiến độ.
- Uy tín chủ đầu tư quyết định chất lượng bàn giao & khả năng giữ giá; CĐT >=10 dự án thường ít rủi ro pháp lý.
- Tiện ích nội khu (hồ bơi, gym, công viên, trường, shophouse) tăng sức hấp dẫn cho gia đình và cho thuê.
- Dự án đang xây (UNDER_CONSTRUCTION) phù hợp nhà đầu tư dài hạn; đã hoàn thiện (COMPLETED) phù hợp ở ngay.
- Các rủi ro phổ biến: tiến độ bàn giao trễ, tranh chấp pháp lý, mật độ xây dựng cao, giá thứ cấp khó tăng.

Bắt buộc trả về **JSON thuần** đúng schema, KHÔNG bọc markdown, KHÔNG giải thích thêm:
{
  "advice": {
    "summary": "<≤ 2 câu tổng quan tiếng Việt>",
    "pros": ["<cụm ngắn>", ...],        // 3-5 mục
    "cons": ["<cụm ngắn>", ...],        // 2-4 mục
    "suitableFor": ["<nhãn persona>", ...] // 2-4 mục, tiếng Việt
  }
}

Các nhãn persona tham khảo: "Gia đình có con nhỏ", "Gia đình trẻ", "Người đi làm xa", "Người trẻ/độc thân", "Yêu thiên nhiên", "Nhà đầu tư dài hạn", "Ưu tiên an ninh", "Người lớn tuổi". Không bắt buộc dùng đúng từ, có thể tuỳ biến cho phù hợp.`;

@QueryHandler(GetProjectAiAdviceQuery)
export class GetProjectAiAdviceHandler
  implements IQueryHandler<GetProjectAiAdviceQuery, ProjectAiAdviceResponse>
{
  constructor(
    @Inject(PROJECT_REPOSITORY)
    private readonly projectRepo: IProjectRepository,
    private readonly queryBus: QueryBus,
    @Inject(AI_CONFIG_PROVIDER)
    private readonly aiConfig: IAIConfigProvider,
    private readonly logger: LoggerService,
  ) {}

  async execute(
    query: GetProjectAiAdviceQuery,
  ): Promise<ProjectAiAdviceResponse> {
    const project = await this.projectRepo.findDetailById(query.projectId);
    if (!project) {
      throw new DomainException(
        ErrorCode.PROJECT_NOT_FOUND,
        `Không tìm thấy dự án ${query.projectId}`,
        HttpStatus.NOT_FOUND,
      );
    }

    const [poisResult, score] = await Promise.all([
      this.fetchPois(project),
      this.fetchScore(project),
    ]);

    const settings = await this.aiConfig.getAiConfig();
    if (!settings.apiKey) {
      throw new DomainException(
        ErrorCode.AI_NOT_CONFIGURED,
        'Quản trị viên chưa cấu hình Claude API. Vào /admin/settings/ai để thiết lập.',
        HttpStatus.SERVICE_UNAVAILABLE,
      );
    }

    const userPrompt = buildUserPrompt(project, poisResult, score);

    const { text, usage } = await callAnthropicJson({
      apiUrl: settings.apiUrl,
      apiKey: settings.apiKey,
      model: settings.model,
      systemPrompt: SYSTEM_PROMPT,
      userPrompt,
      logger: this.logger,
      tag: 'project-ai-advice',
    });

    const obj = parseJsonObject(text);
    const advice = parseAdvice(obj['advice']);

    return {
      advice,
      model: settings.model,
      cacheHit: usage.cacheRead > 0,
      cacheUsage: usage,
    };
  }

  private async fetchPois(
    project: ProjectDetailData,
  ): Promise<NearbyPOIsResultDto | null> {
    const { latitude, longitude } = project;
    if (latitude == null || longitude == null) return null;
    try {
      return await this.queryBus.execute<
        GetNearbyPOIsQuery,
        NearbyPOIsResultDto
      >(new GetNearbyPOIsQuery(latitude, longitude, 2000, 30));
    } catch (err) {
      this.logger.warn(
        `[project-ai-advice] fetchPois failed: ${err instanceof Error ? err.message : err}`,
      );
      return null;
    }
  }

  private async fetchScore(
    project: ProjectDetailData,
  ): Promise<NeighborhoodScoreResult | null> {
    const { district, city } = project;
    if (!district || !city) return null;
    try {
      return await this.queryBus.execute<
        GetNeighborhoodScoreQuery,
        NeighborhoodScoreResult | null
      >(new GetNeighborhoodScoreQuery(district, city));
    } catch (err) {
      this.logger.warn(
        `[project-ai-advice] fetchScore failed: ${err instanceof Error ? err.message : err}`,
      );
      return null;
    }
  }
}

// --------------------------------------------------------------------------
// Prompt construction
// --------------------------------------------------------------------------

function buildUserPrompt(
  project: ProjectDetailData,
  poisResult: NearbyPOIsResultDto | null,
  score: NeighborhoodScoreResult | null,
): string {
  const lines: string[] = [];

  lines.push('### THÔNG TIN DỰ ÁN');
  lines.push(`- Tên dự án: ${project.name}`);
  lines.push(`- Chủ đầu tư: ${project.developer}`);
  lines.push(`- Trạng thái: ${project.status}`);
  lines.push(`- Địa chỉ: ${project.address}, ${project.ward ?? ''}, ${project.district}, ${project.city}`);
  if (project.totalUnits) lines.push(`- Tổng số căn: ${project.totalUnits}`);
  if (project.completedUnits != null)
    lines.push(`- Đã hoàn thành: ${project.completedUnits} căn`);
  if (project.totalArea != null) lines.push(`- Tổng diện tích: ${project.totalArea} m²`);
  if (project.buildingCount != null) lines.push(`- Số block: ${project.buildingCount}`);
  if (project.floorCount != null) lines.push(`- Số tầng: ${project.floorCount}`);
  if (project.startDate) {
    lines.push(`- Khởi công: ${project.startDate.toISOString().slice(0, 10)}`);
  }
  if (project.completionDate) {
    lines.push(`- Dự kiến bàn giao: ${project.completionDate.toISOString().slice(0, 10)}`);
  }
  if (project.minPrice != null && project.maxPrice != null) {
    lines.push(
      `- Dải giá: ${project.minPrice.toString()} – ${project.maxPrice.toString()} VND`,
    );
  }
  if (project.tags && project.tags.length > 0) {
    lines.push(`- Tags: ${project.tags.join(', ')}`);
  }
  if (project.isVerified) lines.push('- Đã xác minh pháp lý: Có');

  if (project.description) {
    const snippet = project.description.slice(0, 600);
    lines.push('');
    lines.push('### MÔ TẢ DỰ ÁN (rút gọn)');
    lines.push(snippet);
  }

  if (project.amenities && isRecord(project.amenities)) {
    const amenityList = flattenAmenities(project.amenities).slice(0, 20);
    if (amenityList.length > 0) {
      lines.push('');
      lines.push('### TIỆN ÍCH NỘI KHU');
      lines.push(`- ${amenityList.join(', ')}`);
    }
  }

  if (project.unitTypes && isRecord(project.unitTypes)) {
    const types = Object.keys(project.unitTypes);
    if (types.length > 0) {
      lines.push('');
      lines.push('### LOẠI HÌNH CĂN HỘ');
      lines.push(`- ${types.join(', ')}`);
    }
  }

  if (project.suitableFor && project.suitableFor.length > 0) {
    lines.push('');
    lines.push(`### CHỦ ĐẦU TƯ GỢI Ý PHÙ HỢP VỚI: ${project.suitableFor.join(', ')}`);
  }
  if (project.whyThisLocation) {
    lines.push('');
    lines.push(`### GHI CHÚ CĐT VỀ VỊ TRÍ: ${project.whyThisLocation}`);
  }

  if (poisResult && poisResult.pois.length > 0) {
    const counts = countByCategory(poisResult.pois);
    const closest = poisResult.pois.slice(0, 5);
    lines.push('');
    lines.push('### ĐIỂM QUAN TÂM LÂN CẬN (bán kính 2km)');
    const countLine = Object.entries(counts)
      .map(([cat, n]) => `${cat}: ${n}`)
      .join(', ');
    lines.push(`- Số lượng theo nhóm: ${countLine}`);
    lines.push('- 5 điểm gần nhất:');
    for (const poi of closest) {
      lines.push(`  • ${poi.name} (${poi.category}) — ${Math.round(poi.distance)} m`);
    }
  }

  if (score) {
    lines.push('');
    lines.push('### ĐIỂM KHU VỰC (thang 0–10)');
    lines.push(`- Giáo dục: ${score.educationScore.toFixed(1)}`);
    lines.push(`- Y tế: ${score.healthcareScore.toFixed(1)}`);
    lines.push(`- Giao thông: ${score.transportScore.toFixed(1)}`);
    lines.push(`- Mua sắm: ${score.shoppingScore.toFixed(1)}`);
    lines.push(`- Môi trường: ${score.greeneryScore.toFixed(1)}`);
    lines.push(`- An ninh: ${score.safetyScore.toFixed(1)}`);
    lines.push(`- Tổng: ${score.totalScore.toFixed(1)}`);
  }

  lines.push('');
  lines.push('Hãy trả về JSON đúng schema đã quy định.');
  return lines.join('\n');
}

function countByCategory(pois: NearbyPOIDto[]): Record<string, number> {
  const out: Record<string, number> = {};
  for (const p of pois) {
    out[p.category] = (out[p.category] ?? 0) + 1;
  }
  return out;
}

/**
 * `amenities` in the DB is a free-form JSONB. Accept both a flat map of
 * {category: string[]} and an array of {name, category} objects; flatten to
 * a list of human-readable strings so the prompt stays compact.
 */
function flattenAmenities(obj: Record<string, unknown>): string[] {
  const out: string[] = [];
  for (const [key, val] of Object.entries(obj)) {
    if (Array.isArray(val)) {
      for (const item of val) {
        if (typeof item === 'string') {
          out.push(item);
        } else if (isRecord(item)) {
          const name = typeof item['name'] === 'string' ? item['name'] : null;
          if (name) out.push(name);
        }
      }
    } else if (typeof val === 'string') {
      out.push(`${key}: ${val}`);
    }
  }
  return out;
}

// --------------------------------------------------------------------------
// Response parsing
// --------------------------------------------------------------------------

function parseAdvice(v: unknown): ProjectAiAdvice {
  if (!isRecord(v)) throw jsonShapeError('advice');
  const summary = asString(v['summary']);
  const pros = asStringArray(v['pros']);
  const cons = asStringArray(v['cons']);
  const suitableFor = asStringArray(v['suitableFor']);
  if (summary == null || !pros || !cons || !suitableFor) {
    throw jsonShapeError('advice');
  }
  return { summary, pros, cons, suitableFor };
}
