// ai/ai.service.ts
import {
  Injectable,
  NotFoundException,
  ForbiddenException,
  BadRequestException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import OpenAI from 'openai';
import dayjs from 'dayjs';

import { Timeline } from '@/entities/timeline.entity';
import { Trip } from '@/entities/trip.entity';
import { NotificationHelper } from '@/helpers/notification.helper';
import { NotificationService } from '@/notification/notification.service';

@Injectable()
export class AIService {
  private openai: OpenAI;

  constructor(
    @InjectRepository(Timeline)
    private timelineRepo: Repository<Timeline>,
    @InjectRepository(Trip)
    private tripRepo: Repository<Trip>,
    private notificationService: NotificationService,
  ) {
    this.openai = new OpenAI({
      apiKey: process.env.OPENAI_API_KEY,
    });
  }

  async optimizeTrip(userId: string, data: any) {
    const { message, tripId, currentTimeline, tripInfo } = data;

    // Validate trip
    const trip = await this.tripRepo.findOne({
      where: { id: tripId },
      relations: ['group', 'group.members', 'group.members.user'],
    });

    if (!trip) {
      throw new NotFoundException('Trip not found');
    }

    // Check if user is leader (uncomment when ready)
    // if (trip.leaderId !== userId) {
    //   throw new ForbiddenException('Only leader can use AI feature');
    // }

    // Parse user intent
    const intent = await this.parseIntent(message, tripInfo, currentTimeline);

    // Process based on intent
    let result;
    switch (intent.action) {
      case 'add':
        // Nếu AI trong parseIntent chưa generate đủ timeline (ít hơn expected),
        // hoặc đây là yêu cầu tạo mới toàn bộ → dùng full generator
        if (this.shouldRegenerateFullTimeline(intent.data?.timeline, tripInfo)) {
          result = await this.handleGenerateAndAdd(userId, trip, message);
        } else {
          result = await this.handleAddTimeline(userId, trip, intent.data);
        }
        break;
      case 'edit':
        result = await this.handleEditTimeline(userId, trip, intent.data);
        break;
      case 'replace':
        // LUÔN dùng full generator cho replace để đảm bảo đủ ngày
        result = await this.handleGenerateAndReplace(userId, trip, message);
        break;
      case 'optimize':
      default:
        result = await this.handleOptimize(userId, trip, intent.data);
        break;
    }

    return result;
  }

  /**
   * Kiểm tra xem timeline từ parseIntent có đủ không.
   * Nếu chuyến đi N ngày mà timeline chỉ có hoạt động cho 1 ngày → cần regenerate.
   */
  private shouldRegenerateFullTimeline(timeline: any[], tripInfo: any): boolean {
    if (!timeline || timeline.length === 0) return true;
    if (!tripInfo?.startDate || !tripInfo?.endDate) return false;
    const totalDays = dayjs(tripInfo.endDate).diff(dayjs(tripInfo.startDate), 'day') + 1;
    if (totalDays <= 1) return false;
    const daysPresent = new Set(timeline.map((t: any) => t.day)).size;
    return daysPresent < totalDays;
  }

  /**
   * Generate toàn bộ lịch trình rồi thêm vào (không xóa hiện tại).
   */
  private async handleGenerateAndAdd(userId: string, trip: Trip, message: string) {
    const generated = await this.generateFullTimeline(trip, message);
    return this.handleAddTimeline(userId, trip, generated);
  }

  /**
   * Generate toàn bộ lịch trình rồi replace.
   * Đây là flow chính cho "Tạo lịch trình mới" để đảm bảo đủ tất cả ngày.
   */
  private async handleGenerateAndReplace(userId: string, trip: Trip, message: string) {
    const generated = await this.generateFullTimeline(trip, message);
    return this.handleReplaceTimeline(userId, trip, generated);
  }

  /**
   * Gọi AI để generate toàn bộ lịch trình cho tất cả ngày của chuyến đi.
   * Sử dụng max_tokens cao và tách theo batch nếu chuyến đi dài.
   */
  private async generateFullTimeline(trip: Trip, message: string): Promise<{ timeline: any[]; message: string }> {
    const totalDays = Math.max(
      trip.endDate ? dayjs(trip.endDate).diff(dayjs(trip.startDate), 'day') + 1 : 3,
      1
    );

    // Với chuyến đi dài hơn 5 ngày, tách thành 2 batch để tránh truncate
    if (totalDays > 5) {
      return this.generateFullTimelineBatched(trip, message, totalDays);
    }

    return this.generateFullTimelineSingle(trip, message, totalDays);
  }

  private async generateFullTimelineSingle(trip: Trip, message: string, totalDays: number): Promise<{ timeline: any[]; message: string }> {
    const prompt = this.buildDetailedTimelinePrompt(
      trip.location || 'Chưa xác định',
      trip.startDate,
      trip.endDate,
      totalDays,
      message
    );

    // Token estimate: ~300 tokens/activity × 5 activities/day × N days + buffer
    const estimatedTokens = Math.min(totalDays * 5 * 300 + 1000, 16000);

    const completion = await this.openai.chat.completions.create({
      model: 'gpt-4o-mini',
      messages: [
        { role: 'system', content: this.buildSystemPrompt() },
        { role: 'user', content: prompt },
      ],
      temperature: 0.7,
      max_tokens: estimatedTokens,
      response_format: { type: 'json_object' },
    });

    const content = completion.choices[0].message.content;
    if (!content) throw new Error('Empty response from OpenAI');

    // Log finish_reason để debug truncation
    const finishReason = completion.choices[0].finish_reason;
    if (finishReason === 'length') {
      console.warn(`[AI] Response truncated at max_tokens=${estimatedTokens} for ${totalDays}-day trip. Consider batching.`);
    }

    const result = JSON.parse(content);
    result.timeline = this.ensureValidTimelineData(result.timeline || []);

    // Validate đủ ngày
    const daysGenerated = new Set(result.timeline.map((t: any) => t.day)).size;
    if (daysGenerated < totalDays) {
      console.warn(`[AI] Only generated ${daysGenerated}/${totalDays} days. finish_reason: ${finishReason}`);
    }

    return result;
  }

  /**
   * Với chuyến đi > 5 ngày, tách batch để tránh token limit.
   * Batch 1: ngày 1 → ceil(N/2), Batch 2: ngày ceil(N/2)+1 → N
   */
  private async generateFullTimelineBatched(
    trip: Trip,
    message: string,
    totalDays: number
  ): Promise<{ timeline: any[]; message: string }> {
    const midDay = Math.ceil(totalDays / 2);

    const buildBatchPrompt = (fromDay: number, toDay: number) => `
Tạo lịch trình chi tiết cho chuyến đi tại ${trip.location || 'điểm đến'}.
Yêu cầu: ${message}
CHỈ tạo cho Ngày ${fromDay} đến Ngày ${toDay} (tổng chuyến đi ${totalDays} ngày, từ ${dayjs(trip.startDate).format('DD/MM/YYYY')} đến ${dayjs(trip.endDate).format('DD/MM/YYYY')}).

Mỗi ngày tối thiểu 5 hoạt động. Các ngày bắt đầu từ:
${Array.from({ length: toDay - fromDay + 1 }, (_, i) => {
  const d = fromDay + i;
  return `  - Ngày ${d}: ${dayjs(trip.startDate).add(d - 1, 'day').format('dddd DD/MM/YYYY')}`;
}).join('\n')}

Trả về JSON: { "timeline": [ { "day": N, "time": "HH:mm", "title": "...", "description": "📍 Địa chỉ:...\\n🕐 Giờ mở cửa:...\\n💰 Chi phí:...\\n✨ Điểm nổi bật:...\\n⚠️ Lưu ý:...", "notify": true/false } ] }
CHỈ TRẢ VỀ JSON.`.trim();

    const [batch1, batch2] = await Promise.all([
      this.openai.chat.completions.create({
        model: 'gpt-4o-mini',
        messages: [
          { role: 'system', content: this.buildSystemPrompt() },
          { role: 'user', content: buildBatchPrompt(1, midDay) },
        ],
        temperature: 0.7,
        max_tokens: 8000,
        response_format: { type: 'json_object' },
      }),
      this.openai.chat.completions.create({
        model: 'gpt-4o-mini',
        messages: [
          { role: 'system', content: this.buildSystemPrompt() },
          { role: 'user', content: buildBatchPrompt(midDay + 1, totalDays) },
        ],
        temperature: 0.7,
        max_tokens: 8000,
        response_format: { type: 'json_object' },
      }),
    ]);

    const parse = (completion: any) => {
      const content = completion.choices[0].message.content;
      if (!content) return [];
      try {
        const parsed = JSON.parse(content);
        return this.ensureValidTimelineData(parsed.timeline || []);
      } catch {
        return [];
      }
    };

    const combinedTimeline = [...parse(batch1), ...parse(batch2)];
    combinedTimeline.sort((a, b) => a.day - b.day || a.time?.localeCompare?.(b.time) || 0);

    return {
      timeline: combinedTimeline,
      message: `Đã tạo lịch trình ${totalDays} ngày tại ${trip.location} với ${combinedTimeline.length} hoạt động chi tiết!`,
    };
  }

  private async parseIntent(message: string, tripInfo: any, currentTimeline: any[]) {
    try {
      // parseIntent CHỈ xác định action + extract thông tin cần thiết.
      // KHÔNG generate toàn bộ timeline ở đây (để tránh token limit cắt ngang).
      // Với action=replace/add "tạo mới", flow sẽ gọi generateFullTimeline riêng.
      const prompt = `
        Phân tích yêu cầu của người dùng và xác định intent:

        Yêu cầu: "${message}"

        Thông tin chuyến đi:
        - Điểm đến: ${tripInfo.destination || 'Chưa xác định'}
        - Ngày bắt đầu: ${tripInfo.startDate || 'Chưa xác định'}
        - Ngày kết thúc: ${tripInfo.endDate || 'Chưa xác định'}
        - Tên chuyến đi: ${tripInfo.name || 'Chưa xác định'}

        Lịch trình hiện tại (${currentTimeline.length} hoạt động):
        ${currentTimeline.length > 0 ? JSON.stringify(currentTimeline.slice(0, 5), null, 2) + (currentTimeline.length > 5 ? '\n...và các hoạt động khác' : '') : 'Chưa có lịch trình'}

        Xác định action:
        1. "replace": Tạo mới hoàn toàn / thay thế toàn bộ lịch trình
        2. "add": Thêm một vài hoạt động cụ thể vào lịch trình hiện tại
        3. "edit": Chỉnh sửa một hoạt động đã có
        4. "optimize": Tối ưu hóa/sắp xếp lại lịch trình hiện tại

        Quy tắc:
        - "Tạo lịch trình", "Lên kế hoạch mới", "Tạo mới" → action = "replace"
        - "Thêm [địa điểm cụ thể]", "Thêm hoạt động X" → action = "add"  
        - "Sửa", "Cập nhật hoạt động Y" → action = "edit"
        - "Tối ưu", "Sắp xếp lại", "Cải thiện" → action = "optimize"

        Trả về JSON:
        {
          "action": "replace|add|edit|optimize",
          "data": {
            "timeline": [],
            "message": "Tôi sẽ [mô tả ngắn gọn hành động]",
            "editTarget": "id nếu action=edit"
          }
        }

        QUAN TRỌNG:
        - Với action = "replace" hoặc "add" cho toàn bộ lịch trình: để "timeline": [] (hệ thống sẽ generate riêng)
        - Với action = "edit": cung cấp timeline với 1 item đã được sửa
        - Với action = "add" cho 1-2 hoạt động cụ thể: cung cấp timeline với các item đó
        - Chỉ trả về JSON, không có text khác.
      `;

      const completion = await this.openai.chat.completions.create({
        model: "gpt-4o-mini",
        messages: [
          {
            role: "system",
            content: this.buildSystemPrompt(),
          },
          {
            role: "user",
            content: prompt
          }
        ],
        temperature: 0.3,
        max_tokens: 1000,
        response_format: { type: "json_object" },
      });

      const content = completion.choices[0].message.content;
      if (!content) {
        throw new Error('Empty response from OpenAI');
      }

      let result = JSON.parse(content);
      
      // Đảm bảo dữ liệu luôn có đầy đủ các trường
      result = this.ensureValidIntentData(result);
      
      return result;
    } catch (error) {
      console.error('Parse Intent Error:', error);
      
      try {
        return await this.parseIntentFallback(message, tripInfo, currentTimeline);
      } catch (fallbackError) {
        console.error('Fallback Parse Intent Error:', fallbackError);
        // Trả về dữ liệu mặc định với đầy đủ các trường
        return {
          action: 'optimize',
          data: {
            timeline: [
              {
                day: 1,
                time: '08:00',
                title: 'Hoạt động mới',
                description: 'Mô tả hoạt động',
                notify: false,
              }
            ],
            message: 'Tôi sẽ tối ưu hóa lịch trình cho bạn.',
          },
        };
      }
    }
  }

  private async parseIntentFallback(message: string, tripInfo: any, currentTimeline: any[]) {
    const prompt = `
      Phân tích yêu cầu: "${message}"
      Điểm đến: ${tripInfo.destination || 'Chưa xác định'}
      Lịch trình hiện tại: ${currentTimeline.length} hoạt động

      Xác định action: replace (tạo mới toàn bộ) | add (thêm vài hoạt động) | edit (sửa 1 hoạt động) | optimize (tối ưu hiện tại)
      
      Trả về JSON: { "action": "replace|add|edit|optimize", "data": { "timeline": [], "message": "mô tả ngắn" } }
      Với action=replace hoặc add toàn bộ: để timeline = [].
      Chỉ trả về JSON.
    `;

    const completion = await this.openai.chat.completions.create({
      model: "gpt-4o-mini",
      messages: [
        {
          role: "system",
          content: "Bạn là trợ lý AI chuyên phân tích intent du lịch. LUÔN TRẢ VỀ JSON HỢP LỆ với đầy đủ các trường bắt buộc. Khi tạo timeline, luôn gợi ý tên địa điểm/nhà hàng cụ thể trong description."
        },
        {
          role: "user",
          content: prompt
        }
      ],
      temperature: 0.3,
      max_tokens: 500,
    });

    const content = completion.choices[0].message.content;
    if (!content) {
      throw new Error('Empty response from OpenAI');
    }

    const jsonMatch = content.match(/\{[\s\S]*\}/);
    if (!jsonMatch) {
      throw new Error('No JSON found in response');
    }

    let result = JSON.parse(jsonMatch[0]);
    result = this.ensureValidIntentData(result);
    
    return result;
  }

  private ensureValidIntentData(intent: any): any {
    // Đảm bảo action hợp lệ
    const validActions = ['add', 'edit', 'replace', 'optimize'];
    if (!intent.action || !validActions.includes(intent.action)) {
      intent.action = 'optimize';
    }

    // Đảm bảo data tồn tại
    if (!intent.data) {
      intent.data = {
        timeline: [],
        message: '',
      };
    }

    // Đảm bảo timeline có ít nhất 1 item với đầy đủ các trường
    if (!intent.data.timeline || intent.data.timeline.length === 0) {
      intent.data.timeline = [
        {
          day: 1,
          time: '08:00',
          title: 'Hoạt động mới',
          description: 'Mô tả hoạt động',
          notify: false,
        }
      ];
    }

    // Normalize từng timeline item
    intent.data.timeline = intent.data.timeline.map((item: any) => {
      // Đảm bảo time có giá trị và đúng format HH:mm
      let timeStr = item.time || '08:00';
      if (!/^\d{2}:\d{2}$/.test(timeStr)) {
        const timeMatch = timeStr.match(/(\d{1,2}):(\d{2})/);
        if (timeMatch) {
          timeStr = `${timeMatch[1].padStart(2, '0')}:${timeMatch[2]}`;
        } else {
          timeStr = '08:00';
        }
      }
      
      return {
        day: Number(item.day) || 1,
        time: timeStr,
        title: item.title || 'Hoạt động mới',
        description: item.description || '',
        notify: item.notify || false,
      };
    });

    // Đảm bảo message có giá trị
    if (!intent.data.message) {
      const actionMessages = {
        add: 'Thêm hoạt động mới vào lịch trình.',
        edit: 'Chỉnh sửa hoạt động trong lịch trình.',
        replace: 'Thay thế toàn bộ lịch trình.',
        optimize: 'Tối ưu hóa lịch trình.',
      };
      intent.data.message = actionMessages[intent.action] || 'Đã nhận được yêu cầu của bạn.';
    }

    return intent;
  }

  private ensureValidTimelineData(timelines: any[]): any[] {
    if (!timelines || timelines.length === 0) {
      return [
        {
          day: 1,
          time: '08:00',
          title: 'Hoạt động mới',
          description: '',
          notify: false,
        }
      ];
    }

    return timelines.map((item: any) => {
      // Đảm bảo time có giá trị và đúng format HH:mm
      let timeStr = item.time || '08:00';
      if (!/^\d{2}:\d{2}$/.test(timeStr)) {
        const timeMatch = timeStr.match(/(\d{1,2}):(\d{2})/);
        if (timeMatch) {
          timeStr = `${timeMatch[1].padStart(2, '0')}:${timeMatch[2]}`;
        } else {
          timeStr = '08:00';
        }
      }
      
      return {
        day: Number(item.day) || 1,
        time: timeStr,
        title: item.title || 'Hoạt động mới',
        description: item.description || '',
        notify: item.notify || false,
      };
    });
  }

  /**
   * Chuyển đổi string time thành Date object
   * Time format: "HH:mm" -> Date object với giờ và phút tương ứng
   */
  private convertTimeStringToDate(timeStr: string): Date {
    const date = new Date();
    // Parse time string "HH:mm"
    const parts = timeStr.match(/(\d{1,2}):(\d{2})/);
    if (!parts) {
      // Nếu không parse được, mặc định 08:00
      date.setHours(8, 0, 0, 0);
      return date;
    }

    const hours = parseInt(parts[1], 10);
    const minutes = parseInt(parts[2], 10);
    date.setHours(hours, minutes, 0, 0);
    
    return date;
  }

  /**
   * Tạo scheduledAt từ startDate, day và time
   */
  private buildScheduledAtWithTime(startDate: Date, day: number, timeStr: string): Date {
    try {
      // Tạo date từ startDate
      const baseDate = new Date(startDate);
      // Cộng thêm day - 1 ngày
      baseDate.setDate(baseDate.getDate() + (day - 1));
      
      // Parse time
      const parts = timeStr.match(/(\d{1,2}):(\d{2})/);
      if (parts) {
        const hours = parseInt(parts[1], 10);
        const minutes = parseInt(parts[2], 10);
        baseDate.setHours(hours, minutes, 0, 0);
      } else {
        // Mặc định 08:00
        baseDate.setHours(8, 0, 0, 0);
      }
      
      return baseDate;
    } catch (error) {
      console.error('Error building scheduledAt:', error);
      // Fallback: tạo date hiện tại
      return new Date();
    }
  }

  /**
   * System prompt dùng chung cho tất cả AI calls liên quan đến lịch trình du lịch.
   * Ràng buộc AI phải gợi ý địa điểm, nhà hàng cụ thể với đầy đủ thông tin thực tế.
   */
  private buildSystemPrompt(): string {
    return `Bạn là chuyên gia lập kế hoạch du lịch với 10 năm kinh nghiệm thực tế tại các điểm đến nổi tiếng khắp Việt Nam và thế giới.

NGUYÊN TẮC BẮT BUỘC khi tạo lịch trình:

1. LUÔN đặt tên địa điểm/nhà hàng CỤ THỂ, có thật trong title (ví dụ: "Ăn sáng tại Phở Thìn Lò Đúc", "Tham quan Hội An Ancient Town", "Cà phê tại The Workshop Coffee").
   - KHÔNG dùng tên chung chung như "nhà hàng địa phương", "quán ăn ngon", "địa điểm tham quan".

2. Trường description PHẢI đầy đủ 5 thành phần sau, mỗi thành phần trên một dòng mới:
   📍 Địa chỉ: [địa chỉ cụ thể, số nhà, đường, quận/huyện]
   🕐 Giờ mở cửa: [ví dụ: 07:00 – 22:00, hoặc mở cả ngày]
   💰 Chi phí: [ví dụ: ~50.000–120.000 VNĐ/người, hoặc Miễn phí]
   ✨ Điểm nổi bật: [2–3 điều đặc sắc, món ngon nên thử, hoạt động không thể bỏ qua]
   ⚠️ Lưu ý: [tips thực tế như đặt bàn trước, mặc trang phục phù hợp, giờ đông khách...]

3. PHÂN BỔ hợp lý mỗi ngày theo khung giờ:
   - Sáng (06:30 – 11:30): ăn sáng + tham quan buổi sáng (mát mẻ, ít đông)
   - Trưa (11:30 – 13:30): ăn trưa + nghỉ ngơi
   - Chiều (13:30 – 17:30): tham quan, mua sắm, hoạt động ngoài trời
   - Tối (18:00 – 22:00): ăn tối + giải trí / dạo phố / bar / show

4. Mỗi ngày có ÍT NHẤT 5 hoạt động, cân bằng giữa: ẩm thực – tham quan – vui chơi giải trí.

5. Gợi ý nhà hàng/quán ăn phải đặc trưng vùng miền, có tiếng, phù hợp tầm giá du lịch phổ thông.

6. TUYỆT ĐỐI KHÔNG tạo time nằm trong khoảng 00:00 – 05:59. Time hợp lệ: 06:00 – 22:00.

7. LUÔN TRẢ VỀ JSON HỢP LỆ, không có text thừa bên ngoài JSON.`;
  }

  /**
   * Tạo prompt chi tiết cho việc tạo lịch trình.
   * Được gọi từ optimizeWithAI và optimizeWithAIFallback.
   */
  private buildDetailedTimelinePrompt(
    destination: string,
    startDate: Date,
    endDate: Date,
    numDays: number,
    preferences?: string
  ): string {
    const start = dayjs(startDate);
    const end = dayjs(endDate);
    const totalDays = Math.max(end.diff(start, 'day') + 1, 1);

    // Xây dựng danh sách ngày để AI biết chính xác ngày nào là ngày bao nhiêu
    const daysList: string[] = [];
    for (let i = 1; i <= Math.min(totalDays, 10); i++) {
      daysList.push(`  - Ngày ${i}: ${start.add(i - 1, 'day').format('dddd, DD/MM/YYYY')}`);
    }

    return `
Hãy tạo LỊCH TRÌNH DU LỊCH CHI TIẾT cho chuyến đi sau:

=== THÔNG TIN CHUYẾN ĐI ===
- Điểm đến: ${destination || 'Chưa xác định'}
- Ngày bắt đầu: ${startDate ? start.format('DD/MM/YYYY') : 'Chưa xác định'}
- Ngày kết thúc: ${endDate ? end.format('DD/MM/YYYY') : 'Chưa xác định'}
- Tổng số ngày: ${totalDays} ngày
- Danh sách ngày:
${daysList.join('\n')}
- Yêu cầu / Sở thích đặc biệt: ${preferences || 'Không có yêu cầu đặc biệt'}

=== YÊU CẦU BẮT BUỘC ===
- Mỗi ngày tối thiểu 5 hoạt động (sáng + trưa + chiều + tối)
- title: tên địa điểm/hoạt động CỤ THỂ (VD: "Ăn sáng tại Bánh Mì Phượng", "Tham quan Phố Cổ Hội An")
- description: đầy đủ 5 mục:
    📍 Địa chỉ: ...
    🕐 Giờ mở cửa: ...
    💰 Chi phí: ...
    ✨ Điểm nổi bật: ...
    ⚠️ Lưu ý: ...
- notify: true nếu cần đặt trước / check-in / di chuyển xa
- Ưu tiên địa điểm đặc trưng của ${destination}, nhà hàng nổi tiếng địa phương

=== VÍ DỤ MỘT HOẠT ĐỘNG ĐÚNG CHUẨN ===
{
  "day": 1,
  "time": "07:30",
  "title": "Ăn sáng tại Phở Thìn Lò Đúc",
  "description": "📍 Địa chỉ: 13 Lò Đúc, Hai Bà Trưng, Hà Nội\\n🕐 Giờ mở cửa: 06:00 – 10:00 (chỉ phục vụ buổi sáng)\\n💰 Chi phí: ~50.000 – 80.000 VNĐ/người\\n✨ Điểm nổi bật: Phở bò xào tái nổi tiếng Hà Nội hơn 50 năm, nước dùng đậm vị, thịt bò tươi. Luôn đông khách, nên đến sớm.\\n⚠️ Lưu ý: Không nhận đặt bàn, xếp hàng tự do. Chỉ mở đến ~10:00 sáng.",
  "notify": false
}

=== ĐỊNH DẠNG JSON TRẢ VỀ ===
{
  "message": "Tổng quan lịch trình 2–3 câu, nêu điểm nhấn của chuyến đi",
  "timeline": [ /* danh sách tất cả hoạt động theo thứ tự ngày và giờ */ ]
}

CHỈ TRẢ VỀ JSON, KHÔNG CÓ TEXT KHÁC.
    `.trim();
  }
  

  private async handleAddTimeline(userId: string, trip: Trip, data: any) {
    let timelineData = data.timeline || [];
    
    if (timelineData.length === 0) {
      throw new BadRequestException('No timeline data to add');
    }

    // Normalize data - đảm bảo có đầy đủ các trường
    timelineData = this.ensureValidTimelineData(timelineData);

    const createdTimelines: any = [];
    
    for (const item of timelineData) {
      // Chuyển đổi time string thành Date
      const timeDate = this.convertTimeStringToDate(item.time);
      
      // Tạo scheduledAt từ startDate, day và time
      const scheduledAt = this.buildScheduledAtWithTime(
        trip.startDate,
        Number(item.day) || 1,
        item.time || '08:00'
      );
      
      // Đảm bảo các trường bắt buộc có giá trị
      const validatedItem = {
        day: Number(item.day) || 1,
        time: timeDate,
        title: item.title || 'Hoạt động mới',
        description: item.description || '',
        notify: item.notify || false,
        scheduledAt: scheduledAt,
      };

      const timeline = this.timelineRepo.create({
        ...validatedItem,
        trip,
        createdBy: { id: userId },
      } as Partial<Timeline>);

      const savedTimeline = await this.timelineRepo.save(timeline);
      createdTimelines.push(savedTimeline);
    }

    if (trip.group && createdTimelines.length > 0) {
      await this.sendNotifications(userId, trip, createdTimelines);
    }

    return {
      message: data.message || `✅ Đã thêm ${createdTimelines.length} hoạt động mới vào lịch trình!`,
      timeline: createdTimelines,
      action: 'add',
    };
  }

  private async handleEditTimeline(userId: string, trip: Trip, data: any) {
    let timelineData = data.timeline || [];
    const editTarget = data.editTarget;

    if (!editTarget) {
      throw new BadRequestException('Edit target ID is required');
    }

    if (timelineData.length === 0) {
      throw new BadRequestException('No timeline data to update');
    }

    // Normalize data
    timelineData = this.ensureValidTimelineData(timelineData);

    const existingTimeline = await this.timelineRepo.findOne({
      where: { id: editTarget },
      relations: ['createdBy'],
    });

    if (!existingTimeline) {
      throw new NotFoundException('Timeline not found');
    }

    if (existingTimeline.createdBy.id !== userId) {
      throw new ForbiddenException('You do not have permission to edit this timeline');
    }

    const updateData = timelineData[0];
    
    // Chuyển đổi time string thành Date
    const timeDate = this.convertTimeStringToDate(updateData.time || '08:00');
    
    // Tạo scheduledAt từ startDate, day và time
    const scheduledAt = this.buildScheduledAtWithTime(
      trip.startDate,
      Number(updateData.day) || existingTimeline.day || 1,
      updateData.time || '08:00'
    );
    
    // Đảm bảo các trường bắt buộc có giá trị
    const validatedUpdate = {
      day: Number(updateData.day) || existingTimeline.day || 1,
      time: timeDate,
      title: updateData.title || existingTimeline.title || 'Hoạt động',
      description: updateData.description || existingTimeline.description || '',
      notify: updateData.notify !== undefined ? updateData.notify : existingTimeline.notify || false,
      scheduledAt: scheduledAt,
    };

    Object.assign(existingTimeline, validatedUpdate);

    const updatedTimeline = await this.timelineRepo.save(existingTimeline);

    return {
      message: data.message || '✅ Đã cập nhật hoạt động thành công!',
      timeline: [updatedTimeline],
      action: 'edit',
      editTarget: editTarget,
    };
  }

  private async handleReplaceTimeline(userId: string, trip: Trip, data: any) {
    let timelineData = data.timeline || [];

    if (timelineData.length === 0) {
      throw new BadRequestException('No timeline data to replace');
    }

    // Normalize data
    timelineData = this.ensureValidTimelineData(timelineData);

    // Delete all existing timelines
    await this.timelineRepo.delete({ trip: { id: trip.id } });

    const createdTimelines: any = [];
    
    for (const item of timelineData) {
      // Chuyển đổi time string thành Date
      const timeDate = this.convertTimeStringToDate(item.time);
      
      // Tạo scheduledAt từ startDate, day và time
      const scheduledAt = this.buildScheduledAtWithTime(
        trip.startDate,
        Number(item.day) || 1,
        item.time || '08:00'
      );
      
      // Đảm bảo các trường bắt buộc có giá trị
      const validatedItem = {
        day: Number(item.day) || 1,
        time: timeDate,
        title: item.title || 'Hoạt động mới',
        description: item.description || '',
        notify: item.notify || false,
        scheduledAt: scheduledAt,
      };

      const timeline = this.timelineRepo.create({
        ...validatedItem,
        trip,
        createdBy: { id: userId },
      } as Partial<Timeline>);

      const savedTimeline = await this.timelineRepo.save(timeline);
      createdTimelines.push(savedTimeline);
    }

    if (trip.group && createdTimelines.length > 0) {
      await this.sendNotifications(userId, trip, createdTimelines);
    }

    return {
      message: data.message || `✅ Đã thay thế toàn bộ lịch trình với ${createdTimelines.length} hoạt động mới!`,
      timeline: createdTimelines,
      action: 'replace',
    };
  }

  private async handleOptimize(userId: string, trip: Trip, data: any) {
    const currentTimeline = await this.timelineRepo.find({
      where: { trip: { id: trip.id } },
      order: { day: 'ASC', time: 'ASC' },
    });

    if (currentTimeline.length === 0) {
      const replaceData = {
        timeline: data.timeline || [],
        message: data.message || 'Tạo lịch trình mới cho chuyến đi của bạn!',
      };
      return await this.handleReplaceTimeline(userId, trip, replaceData);
    }

    const optimizedData = await this.optimizeWithAI(userId, trip, currentTimeline, data.message);

    // Normalize optimized data
    let normalizedTimeline = this.ensureValidTimelineData(optimizedData.timeline || []);

    const updatedTimelines: any = [];
    for (const item of normalizedTimeline) {
      // Chuyển đổi time string thành Date
      const timeDate = this.convertTimeStringToDate(item.time);
      
      // Tạo scheduledAt từ startDate, day và time
      const scheduledAt = this.buildScheduledAtWithTime(
        trip.startDate,
        Number(item.day) || 1,
        item.time || '08:00'
      );
      
      // Đảm bảo các trường bắt buộc có giá trị
      const validatedItem = {
        day: Number(item.day) || 1,
        time: timeDate,
        title: item.title || 'Hoạt động',
        description: item.description || '',
        notify: item.notify || false,
        scheduledAt: scheduledAt,
      };

      // Tìm timeline phù hợp để update
      const existing = currentTimeline.find(
        t => t.day === validatedItem.day && 
        t.time.getHours() === timeDate.getHours() && 
        t.time.getMinutes() === timeDate.getMinutes()
      );

      if (existing) {
        Object.assign(existing, validatedItem);
        const updated = await this.timelineRepo.save(existing);
        updatedTimelines.push(updated);
      } else {
        const newTimeline = this.timelineRepo.create({
          ...validatedItem,
          trip,
          createdBy: { id: userId },
        } as Partial<Timeline>);
        const saved = await this.timelineRepo.save(newTimeline);
        updatedTimelines.push(saved);
      }
    }

    return {
      message: optimizedData.message || '✅ Đã tối ưu hóa lịch trình!',
      timeline: updatedTimelines,
      action: 'optimize',
    };
  }

  private async optimizeWithAI(userId: string, trip: Trip, currentTimeline: any[], message: string) {
    try {
      // Chuẩn bị dữ liệu timeline hiện tại để gửi cho AI
      const timelineForAI = currentTimeline.map(item => ({
        day: item.day,
        time: item.time instanceof Date 
          ? `${String(item.time.getHours()).padStart(2, '0')}:${String(item.time.getMinutes()).padStart(2, '0')}`
          : item.time,
        title: item.title,
        description: item.description,
        notify: item.notify,
      }));

      // Sử dụng prompt chi tiết
      const prompt = this.buildDetailedTimelinePrompt(
        trip.location || 'Chưa xác định',
        trip.startDate,
        trip.endDate,
        Math.max(trip.endDate ? dayjs(trip.endDate).diff(dayjs(trip.startDate), 'day') + 1 : 3, 1),
        message
      );

      // Token estimate theo số ngày
      const totalDays = Math.max(
        trip.endDate ? dayjs(trip.endDate).diff(dayjs(trip.startDate), 'day') + 1 : 3,
        1,
      );
      const estimatedTokens = Math.min(totalDays * 5 * 300 + 1000, 16000);

      const completion = await this.openai.chat.completions.create({
        model: "gpt-4o-mini",
        messages: [
          {
            role: "system",
            content: this.buildSystemPrompt(),
          },
          {
            role: "user",
            content: prompt
          }
        ],
        temperature: 0.7,
        max_tokens: estimatedTokens,
        response_format: { type: "json_object" },
      });

      const content = completion.choices[0].message.content;
      if (!content) {
        throw new Error('Empty response from OpenAI');
      }

      const result = JSON.parse(content);
      
      // Đảm bảo dữ liệu luôn có đầy đủ các trường
      if (result.timeline) {
        result.timeline = this.ensureValidTimelineData(result.timeline);
      }
      
      return result;
    } catch (error) {
      console.error('Optimize With AI Error:', error);
      
      try {
        return await this.optimizeWithAIFallback(trip, currentTimeline, message);
      } catch (fallbackError) {
        console.error('Optimize Fallback Error:', fallbackError);
        return {
          message: 'Không thể tối ưu hóa, giữ nguyên lịch trình hiện tại.',
          timeline: currentTimeline.map(item => ({
            day: item.day || 1,
            time: item.time instanceof Date 
              ? `${String(item.time.getHours()).padStart(2, '0')}:${String(item.time.getMinutes()).padStart(2, '0')}`
              : '08:00',
            title: item.title || 'Hoạt động',
            description: item.description || '',
            notify: item.notify || false,
          })),
        };
      }
    }
  }

  private async optimizeWithAIFallback(trip: Trip, currentTimeline: any[], message: string) {
    // Sử dụng prompt chi tiết
    const prompt = this.buildDetailedTimelinePrompt(
      trip.location || 'Chưa xác định',
      trip.startDate,
      trip.endDate,
      Math.max(trip.endDate ? dayjs(trip.endDate).diff(dayjs(trip.startDate), 'day') + 1 : 3, 1),
      message
    );

    const completion = await this.openai.chat.completions.create({
      model: "gpt-4o-mini",
      messages: [
        {
          role: "system",
          content: this.buildSystemPrompt(),
        },
        {
          role: "user",
          content: prompt
        }
      ],
      temperature: 0.7,
      max_tokens: Math.min(
        Math.max(trip.endDate ? dayjs(trip.endDate).diff(dayjs(trip.startDate), 'day') + 1 : 3, 1) * 5 * 300 + 1000,
        16000,
      ),
    });

    const content = completion.choices[0].message.content;
    if (!content) {
      throw new Error('Empty response from OpenAI');
    }

    const jsonMatch = content.match(/\{[\s\S]*\}/);
    if (!jsonMatch) {
      throw new Error('No JSON found in response');
    }

    const result = JSON.parse(jsonMatch[0]);
    
    if (result.timeline) {
      result.timeline = this.ensureValidTimelineData(result.timeline);
    }
    
    return result;
  }

  private async sendNotifications(userId: string, trip: Trip, timelines: Timeline[]) {
    if (!trip.group) return;

    const payload: any[] = [];

    trip.group.members.forEach((m) => {
      if (m.user.id !== userId) {
        const notification = NotificationHelper.newTimeline({
          timeline: timelines[0],
          trip,
          group: trip.group,
          user: m.user,
          createdBy: userId,
        });
        payload.push(notification);
      }
    });

    if (payload.length > 0) {
      await this.notificationService.createManyAndPush(trip.group.id, payload);
    }
  }

  async clearTimeline(tripId: string, userId: string) {
    const trip = await this.tripRepo.findOne({
      where: { id: tripId },
    });

    if (!trip) {
      throw new NotFoundException('Trip not found');
    }

    // Check if user is leader (uncomment when ready)
    // if (trip.leaderId !== userId) {
    //   throw new ForbiddenException('Only leader can clear timeline');
    // }

    const result = await this.timelineRepo.delete({ trip: { id: tripId } });
    
    return {
      message: '🗑️ Đã xóa toàn bộ lịch trình',
      deletedCount: result.affected || 0,
    };
  }
}