import * as fs from "fs";
import * as path from "path";
import { v4 as uuidv4 } from "uuid";
import { logger } from "../utils/logger";
import { query } from "../database/db";

const BOOKING_DIR = path.join(__dirname, "../../data/bookings");
const BOOKING_FILE = path.join(BOOKING_DIR, "bookings.json");

// 환경 변수로 데이터 소스 선택
const DATA_SOURCE = process.env.BOOKING_DATA_SOURCE || "file";

export interface BookingRequest {
  id: string;
  customerName: string;
  customerPhone: string;
  pickupLocation: string;
  dropoffLocation: string;
  scheduledTime: string;
  vehicleType: "truck" | "van" | "car";
  cargoType?: string;
  weight?: number;
  status: "pending" | "accepted" | "rejected" | "completed";
  priority: "normal" | "urgent";
  createdAt: string;
  updatedAt: string;
  acceptedAt?: string;
  rejectedAt?: string;
  completedAt?: string;
  notes?: string;
  estimatedCost?: number;
}

/**
 * 예약 요청 관리 서비스 (File/DB 하이브리드)
 */
export class BookingService {
  private static instance: BookingService;

  private constructor() {
    if (DATA_SOURCE === "file") {
      this.ensureDataDirectory();
      this.generateMockData();
    }
    logger.info(`📋 예약 요청 데이터 소스: ${DATA_SOURCE.toUpperCase()}`);
  }

  public static getInstance(): BookingService {
    if (!BookingService.instance) {
      BookingService.instance = new BookingService();
    }
    return BookingService.instance;
  }

  private ensureDataDirectory(): void {
    try {
      if (!fs.existsSync(BOOKING_DIR)) {
        fs.mkdirSync(BOOKING_DIR, { recursive: true, mode: 0o755 });
        logger.info(`📁 예약 데이터 디렉토리 생성: ${BOOKING_DIR}`);
      }
      if (!fs.existsSync(BOOKING_FILE)) {
        fs.writeFileSync(BOOKING_FILE, JSON.stringify([], null, 2), {
          mode: 0o644,
        });
        logger.info(`📄 예약 파일 생성: ${BOOKING_FILE}`);
      }
    } catch (error) {
      logger.error(`❌ 예약 디렉토리 생성 실패: ${BOOKING_DIR}`, error);
      throw error;
    }
  }

  private generateMockData(): void {
    const bookings = this.loadBookingsFromFile();
    if (bookings.length > 0) return;

    const mockBookings: BookingRequest[] = [
      {
        id: uuidv4(),
        customerName: "김철수",
        customerPhone: "010-1234-5678",
        pickupLocation: "서울시 강남구 역삼동 123",
        dropoffLocation: "경기도 성남시 분당구 정자동 456",
        scheduledTime: new Date(Date.now() + 2 * 60 * 60 * 1000).toISOString(),
        vehicleType: "truck",
        cargoType: "전자제품",
        weight: 500,
        status: "pending",
        priority: "urgent",
        createdAt: new Date().toISOString(),
        updatedAt: new Date().toISOString(),
        estimatedCost: 150000,
      },
      {
        id: uuidv4(),
        customerName: "이영희",
        customerPhone: "010-9876-5432",
        pickupLocation: "서울시 송파구 잠실동 789",
        dropoffLocation: "인천시 남동구 구월동 321",
        scheduledTime: new Date(Date.now() + 4 * 60 * 60 * 1000).toISOString(),
        vehicleType: "van",
        cargoType: "가구",
        weight: 300,
        status: "pending",
        priority: "normal",
        createdAt: new Date(Date.now() - 10 * 60 * 1000).toISOString(),
        updatedAt: new Date(Date.now() - 10 * 60 * 1000).toISOString(),
        estimatedCost: 80000,
      },
    ];

    this.saveBookingsToFile(mockBookings);
    logger.info(`✅ 예약 목 데이터 생성: ${mockBookings.length}개`);
  }

  public async getAllBookings(filter?: {
    status?: string;
    priority?: string;
  }): Promise<{ bookings: BookingRequest[]; newCount: number }> {
    try {
      const bookings =
        DATA_SOURCE === "database"
          ? await this.loadBookingsFromDB(filter)
          : this.loadBookingsFromFile(filter);

      bookings.sort((a, b) => {
        if (a.priority !== b.priority) return a.priority === "urgent" ? -1 : 1;
        return (
          new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
        );
      });

      const fiveMinutesAgo = new Date(Date.now() - 5 * 60 * 1000);
      const newCount = bookings.filter(
        (b) => b.status === "pending" && new Date(b.createdAt) > fiveMinutesAgo
      ).length;

      return { bookings, newCount };
    } catch (error) {
      logger.error("❌ 예약 목록 조회 오류:", error);
      throw error;
    }
  }

  public async acceptBooking(id: string): Promise<BookingRequest> {
    try {
      if (DATA_SOURCE === "database") {
        return await this.acceptBookingDB(id);
      } else {
        return this.acceptBookingFile(id);
      }
    } catch (error) {
      logger.error("❌ 예약 수락 오류:", error);
      throw error;
    }
  }

  public async rejectBooking(
    id: string,
    reason?: string
  ): Promise<BookingRequest> {
    try {
      if (DATA_SOURCE === "database") {
        return await this.rejectBookingDB(id, reason);
      } else {
        return this.rejectBookingFile(id, reason);
      }
    } catch (error) {
      logger.error("❌ 예약 거절 오류:", error);
      throw error;
    }
  }

  // ==================== DATABASE 메서드 ====================

  private async loadBookingsFromDB(filter?: {
    status?: string;
    priority?: string;
  }): Promise<BookingRequest[]> {
    let sql = `
      SELECT 
        id, customer_name as "customerName", customer_phone as "customerPhone",
        pickup_location as "pickupLocation", dropoff_location as "dropoffLocation",
        scheduled_time as "scheduledTime", vehicle_type as "vehicleType",
        cargo_type as "cargoType", weight, status, priority,
        created_at as "createdAt", updated_at as "updatedAt",
        accepted_at as "acceptedAt", rejected_at as "rejectedAt",
        completed_at as "completedAt", notes, estimated_cost as "estimatedCost"
      FROM booking_requests
      WHERE 1=1
    `;
    const params: any[] = [];
    let paramIndex = 1;

    if (filter?.status) {
      sql += ` AND status = $${paramIndex++}`;
      params.push(filter.status);
    }
    if (filter?.priority) {
      sql += ` AND priority = $${paramIndex++}`;
      params.push(filter.priority);
    }

    const rows = await query(sql, params);
    return rows.map((row: any) => ({
      ...row,
      scheduledTime: new Date(row.scheduledTime).toISOString(),
      createdAt: new Date(row.createdAt).toISOString(),
      updatedAt: new Date(row.updatedAt).toISOString(),
      acceptedAt: row.acceptedAt
        ? new Date(row.acceptedAt).toISOString()
        : undefined,
      rejectedAt: row.rejectedAt
        ? new Date(row.rejectedAt).toISOString()
        : undefined,
      completedAt: row.completedAt
        ? new Date(row.completedAt).toISOString()
        : undefined,
    }));
  }

  private async acceptBookingDB(id: string): Promise<BookingRequest> {
    const rows = await query(
      `UPDATE booking_requests 
       SET status = 'accepted', accepted_at = NOW(), updated_at = NOW()
       WHERE id = $1
       RETURNING 
         id, customer_name as "customerName", customer_phone as "customerPhone",
         pickup_location as "pickupLocation", dropoff_location as "dropoffLocation",
         scheduled_time as "scheduledTime", vehicle_type as "vehicleType",
         cargo_type as "cargoType", weight, status, priority,
         created_at as "createdAt", updated_at as "updatedAt",
         accepted_at as "acceptedAt", notes, estimated_cost as "estimatedCost"`,
      [id]
    );

    if (rows.length === 0) {
      throw new Error(`예약을 찾을 수 없습니다: ${id}`);
    }

    const row = rows[0];
    logger.info(`✅ 예약 수락: ${id} - ${row.customerName}`);
    return {
      ...row,
      scheduledTime: new Date(row.scheduledTime).toISOString(),
      createdAt: new Date(row.createdAt).toISOString(),
      updatedAt: new Date(row.updatedAt).toISOString(),
      acceptedAt: new Date(row.acceptedAt).toISOString(),
    };
  }

  private async rejectBookingDB(
    id: string,
    reason?: string
  ): Promise<BookingRequest> {
    const rows = await query(
      `UPDATE booking_requests 
       SET status = 'rejected', rejected_at = NOW(), updated_at = NOW(), rejection_reason = $2
       WHERE id = $1
       RETURNING 
         id, customer_name as "customerName", customer_phone as "customerPhone",
         pickup_location as "pickupLocation", dropoff_location as "dropoffLocation",
         scheduled_time as "scheduledTime", vehicle_type as "vehicleType",
         cargo_type as "cargoType", weight, status, priority,
         created_at as "createdAt", updated_at as "updatedAt",
         rejected_at as "rejectedAt", notes, estimated_cost as "estimatedCost"`,
      [id, reason]
    );

    if (rows.length === 0) {
      throw new Error(`예약을 찾을 수 없습니다: ${id}`);
    }

    const row = rows[0];
    logger.info(`✅ 예약 거절: ${id} - ${row.customerName}`);
    return {
      ...row,
      scheduledTime: new Date(row.scheduledTime).toISOString(),
      createdAt: new Date(row.createdAt).toISOString(),
      updatedAt: new Date(row.updatedAt).toISOString(),
      rejectedAt: new Date(row.rejectedAt).toISOString(),
    };
  }

  // ==================== FILE 메서드 ====================

  private loadBookingsFromFile(filter?: {
    status?: string;
    priority?: string;
  }): BookingRequest[] {
    try {
      const data = fs.readFileSync(BOOKING_FILE, "utf-8");
      let bookings: BookingRequest[] = JSON.parse(data);

      if (filter?.status) {
        bookings = bookings.filter((b) => b.status === filter.status);
      }
      if (filter?.priority) {
        bookings = bookings.filter((b) => b.priority === filter.priority);
      }

      return bookings;
    } catch (error) {
      logger.error("❌ 예약 파일 로드 오류:", error);
      return [];
    }
  }

  private saveBookingsToFile(bookings: BookingRequest[]): void {
    try {
      fs.writeFileSync(BOOKING_FILE, JSON.stringify(bookings, null, 2));
    } catch (error) {
      logger.error("❌ 예약 파일 저장 오류:", error);
      throw error;
    }
  }

  private acceptBookingFile(id: string): BookingRequest {
    const bookings = this.loadBookingsFromFile();
    const booking = bookings.find((b) => b.id === id);

    if (!booking) {
      throw new Error(`예약을 찾을 수 없습니다: ${id}`);
    }

    booking.status = "accepted";
    booking.acceptedAt = new Date().toISOString();
    booking.updatedAt = new Date().toISOString();

    this.saveBookingsToFile(bookings);
    logger.info(`✅ 예약 수락: ${id} - ${booking.customerName}`);

    return booking;
  }

  private rejectBookingFile(id: string, reason?: string): BookingRequest {
    const bookings = this.loadBookingsFromFile();
    const booking = bookings.find((b) => b.id === id);

    if (!booking) {
      throw new Error(`예약을 찾을 수 없습니다: ${id}`);
    }

    booking.status = "rejected";
    booking.rejectedAt = new Date().toISOString();
    booking.updatedAt = new Date().toISOString();
    if (reason) {
      booking.notes = reason;
    }

    this.saveBookingsToFile(bookings);
    logger.info(`✅ 예약 거절: ${id} - ${booking.customerName}`);

    return booking;
  }
}
