import { Pool } from 'pg';
import pool from '../../../core/config/database';
import type { MaintenanceRecord, MaintenanceSchedule, MaintenanceCategory } from '../domain/maintenance.types';

export class MaintenanceRepository {
  constructor(private readonly db: Pool = pool) {}

  // ========================
  // Row Mappers
  // ========================

  private mapMaintenanceRecord(row: any): MaintenanceRecord {
    return {
      id: row.id,
      userId: row.user_id,
      vehicleId: row.vehicle_id,
      category: row.category,
      subtypes: row.subtypes,
      date: row.date,
      odometerReading: row.odometer_reading,
      cost: row.cost,
      shopName: row.shop_name,
      notes: row.notes,
      createdAt: row.created_at,
      updatedAt: row.updated_at
    };
  }

  private mapMaintenanceSchedule(row: any): MaintenanceSchedule {
    return {
      id: row.id,
      userId: row.user_id,
      vehicleId: row.vehicle_id,
      category: row.category,
      subtypes: row.subtypes,
      intervalMonths: row.interval_months,
      intervalMiles: row.interval_miles,
      lastServiceDate: row.last_service_date,
      lastServiceMileage: row.last_service_mileage,
      nextDueDate: row.next_due_date,
      nextDueMileage: row.next_due_mileage,
      isActive: row.is_active,
      emailNotifications: row.email_notifications,
      scheduleType: row.schedule_type,
      fixedDueDate: row.fixed_due_date,
      reminderDays1: row.reminder_days_1,
      reminderDays2: row.reminder_days_2,
      reminderDays3: row.reminder_days_3,
      createdAt: row.created_at,
      updatedAt: row.updated_at
    };
  }

  // ========================
  // Maintenance Records
  // ========================

  async insertRecord(record: {
    id: string;
    userId: string;
    vehicleId: string;
    category: MaintenanceCategory;
    subtypes: string[];
    date: string;
    odometerReading?: number | null;
    cost?: number | null;
    shopName?: string | null;
    notes?: string | null;
  }): Promise<MaintenanceRecord> {
    const res = await this.db.query(
      `INSERT INTO maintenance_records (
        id, user_id, vehicle_id, category, subtypes, date, odometer_reading, cost, shop_name, notes
      ) VALUES ($1, $2, $3, $4, $5::text[], $6, $7, $8, $9, $10)
      RETURNING *`,
      [
        record.id,
        record.userId,
        record.vehicleId,
        record.category,
        record.subtypes,
        record.date,
        record.odometerReading ?? null,
        record.cost ?? null,
        record.shopName ?? null,
        record.notes ?? null,
      ]
    );
    return this.mapMaintenanceRecord(res.rows[0]);
  }

  async findRecordById(id: string, userId: string): Promise<MaintenanceRecord | null> {
    const res = await this.db.query(
      `SELECT * FROM maintenance_records WHERE id = $1 AND user_id = $2`,
      [id, userId]
    );
    return res.rows[0] ? this.mapMaintenanceRecord(res.rows[0]) : null;
  }

  async findRecordsByUserId(
    userId: string,
    filters?: { vehicleId?: string; category?: MaintenanceCategory }
  ): Promise<MaintenanceRecord[]> {
    const conds: string[] = ['user_id = $1'];
    const params: any[] = [userId];
    let i = 2;

    if (filters?.vehicleId) {
      conds.push(`vehicle_id = $${i++}`);
      params.push(filters.vehicleId);
    }
    if (filters?.category) {
      conds.push(`category = $${i++}`);
      params.push(filters.category);
    }

    const sql = `SELECT * FROM maintenance_records WHERE ${conds.join(' AND ')} ORDER BY date DESC`;
    const res = await this.db.query(sql, params);
    return res.rows.map(row => this.mapMaintenanceRecord(row));
  }

  async findRecordsByVehicleId(vehicleId: string, userId: string): Promise<MaintenanceRecord[]> {
    const res = await this.db.query(
      `SELECT * FROM maintenance_records WHERE vehicle_id = $1 AND user_id = $2 ORDER BY date DESC`,
      [vehicleId, userId]
    );
    return res.rows.map(row => this.mapMaintenanceRecord(row));
  }

  async updateRecord(
    id: string,
    userId: string,
    patch: Partial<Pick<MaintenanceRecord, 'category' | 'subtypes' | 'date' | 'odometerReading' | 'cost' | 'shopName' | 'notes'>>
  ): Promise<MaintenanceRecord | null> {
    const fields: string[] = [];
    const params: any[] = [];
    let i = 1;

    if (patch.category !== undefined) {
      fields.push(`category = $${i++}`);
      params.push(patch.category);
    }
    if (patch.subtypes !== undefined) {
      fields.push(`subtypes = $${i++}::text[]`);
      params.push(patch.subtypes);
    }
    if (patch.date !== undefined) {
      fields.push(`date = $${i++}`);
      params.push(patch.date);
    }
    if (patch.odometerReading !== undefined) {
      fields.push(`odometer_reading = $${i++}`);
      params.push(patch.odometerReading);
    }
    if (patch.cost !== undefined) {
      fields.push(`cost = $${i++}`);
      params.push(patch.cost);
    }
    if (patch.shopName !== undefined) {
      fields.push(`shop_name = $${i++}`);
      params.push(patch.shopName);
    }
    if (patch.notes !== undefined) {
      fields.push(`notes = $${i++}`);
      params.push(patch.notes);
    }

    if (!fields.length) return this.findRecordById(id, userId);

    params.push(id, userId);
    const sql = `UPDATE maintenance_records SET ${fields.join(', ')} WHERE id = $${i++} AND user_id = $${i++} RETURNING *`;
    const res = await this.db.query(sql, params);
    return res.rows[0] ? this.mapMaintenanceRecord(res.rows[0]) : null;
  }

  async batchInsertRecords(
    records: Array<{
      id: string;
      userId: string;
      vehicleId: string;
      category: MaintenanceCategory;
      subtypes: string[];
      date: string;
      odometerReading?: number | null;
      cost?: number | null;
      shopName?: string | null;
      notes?: string | null;
    }>,
    client?: any
  ): Promise<MaintenanceRecord[]> {
    if (records.length === 0) {
      return [];
    }

    // Multi-value INSERT for performance (avoids N round-trips)
    const queryClient = client || this.db;
    const placeholders: string[] = [];
    const values: any[] = [];
    let paramCount = 1;

    records.forEach((record) => {
      const recordParams = [
        record.id,
        record.userId,
        record.vehicleId,
        record.category,
        record.subtypes,
        record.date,
        record.odometerReading ?? null,
        record.cost ?? null,
        record.shopName ?? null,
        record.notes ?? null
      ];

      const placeholder = `($${paramCount++}, $${paramCount++}, $${paramCount++}, $${paramCount++}, $${paramCount++}::text[], $${paramCount++}, $${paramCount++}, $${paramCount++}, $${paramCount++}, $${paramCount++})`;
      placeholders.push(placeholder);
      values.push(...recordParams);
    });

    const query = `
      INSERT INTO maintenance_records (
        id, user_id, vehicle_id, category, subtypes, date, odometer_reading, cost, shop_name, notes
      )
      VALUES ${placeholders.join(', ')}
      RETURNING *
    `;

    const result = await queryClient.query(query, values);
    return result.rows.map((row: any) => this.mapMaintenanceRecord(row));
  }

  async deleteRecord(id: string, userId: string): Promise<void> {
    await this.db.query(
      `DELETE FROM maintenance_records WHERE id = $1 AND user_id = $2`,
      [id, userId]
    );
  }

  // ========================
  // Maintenance Schedules
  // ========================

  async insertSchedule(schedule: {
    id: string;
    userId: string;
    vehicleId: string;
    category: MaintenanceCategory;
    subtypes: string[];
    intervalMonths?: number | null;
    intervalMiles?: number | null;
    lastServiceDate?: string | null;
    lastServiceMileage?: number | null;
    nextDueDate?: string | null;
    nextDueMileage?: number | null;
    isActive: boolean;
    emailNotifications?: boolean;
    scheduleType?: string;
    fixedDueDate?: string | null;
    reminderDays1?: number | null;
    reminderDays2?: number | null;
    reminderDays3?: number | null;
  }): Promise<MaintenanceSchedule> {
    const res = await this.db.query(
      `INSERT INTO maintenance_schedules (
        id, user_id, vehicle_id, category, subtypes, interval_months, interval_miles,
        last_service_date, last_service_mileage, next_due_date, next_due_mileage, is_active, email_notifications,
        schedule_type, fixed_due_date, reminder_days_1, reminder_days_2, reminder_days_3
      ) VALUES ($1, $2, $3, $4, $5::text[], $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18)
      RETURNING *`,
      [
        schedule.id,
        schedule.userId,
        schedule.vehicleId,
        schedule.category,
        schedule.subtypes,
        schedule.intervalMonths ?? null,
        schedule.intervalMiles ?? null,
        schedule.lastServiceDate ?? null,
        schedule.lastServiceMileage ?? null,
        schedule.nextDueDate ?? null,
        schedule.nextDueMileage ?? null,
        schedule.isActive,
        schedule.emailNotifications ?? false,
        schedule.scheduleType ?? 'interval',
        schedule.fixedDueDate ?? null,
        schedule.reminderDays1 ?? null,
        schedule.reminderDays2 ?? null,
        schedule.reminderDays3 ?? null,
      ]
    );
    return this.mapMaintenanceSchedule(res.rows[0]);
  }

  async findScheduleById(id: string, userId: string): Promise<MaintenanceSchedule | null> {
    const res = await this.db.query(
      `SELECT * FROM maintenance_schedules WHERE id = $1 AND user_id = $2`,
      [id, userId]
    );
    return res.rows[0] ? this.mapMaintenanceSchedule(res.rows[0]) : null;
  }

  async findSchedulesByVehicleId(vehicleId: string, userId: string): Promise<MaintenanceSchedule[]> {
    const res = await this.db.query(
      `SELECT * FROM maintenance_schedules WHERE vehicle_id = $1 AND user_id = $2 ORDER BY created_at DESC`,
      [vehicleId, userId]
    );
    return res.rows.map(row => this.mapMaintenanceSchedule(row));
  }

  async findActiveSchedulesByVehicleId(vehicleId: string, userId: string): Promise<MaintenanceSchedule[]> {
    const res = await this.db.query(
      `SELECT * FROM maintenance_schedules WHERE vehicle_id = $1 AND user_id = $2 AND is_active = true ORDER BY created_at DESC`,
      [vehicleId, userId]
    );
    return res.rows.map(row => this.mapMaintenanceSchedule(row));
  }

  async updateSchedule(
    id: string,
    userId: string,
    patch: Partial<Pick<MaintenanceSchedule, 'category' | 'subtypes' | 'intervalMonths' | 'intervalMiles' | 'lastServiceDate' | 'lastServiceMileage' | 'nextDueDate' | 'nextDueMileage' | 'isActive' | 'emailNotifications' | 'scheduleType' | 'fixedDueDate' | 'reminderDays1' | 'reminderDays2' | 'reminderDays3'>>
  ): Promise<MaintenanceSchedule | null> {
    const fields: string[] = [];
    const params: any[] = [];
    let i = 1;

    if (patch.category !== undefined) {
      fields.push(`category = $${i++}`);
      params.push(patch.category);
    }
    if (patch.subtypes !== undefined) {
      fields.push(`subtypes = $${i++}::text[]`);
      params.push(patch.subtypes);
    }
    if (patch.intervalMonths !== undefined) {
      fields.push(`interval_months = $${i++}`);
      params.push(patch.intervalMonths);
    }
    if (patch.intervalMiles !== undefined) {
      fields.push(`interval_miles = $${i++}`);
      params.push(patch.intervalMiles);
    }
    if (patch.lastServiceDate !== undefined) {
      fields.push(`last_service_date = $${i++}`);
      params.push(patch.lastServiceDate);
    }
    if (patch.lastServiceMileage !== undefined) {
      fields.push(`last_service_mileage = $${i++}`);
      params.push(patch.lastServiceMileage);
    }
    if (patch.nextDueDate !== undefined) {
      fields.push(`next_due_date = $${i++}`);
      params.push(patch.nextDueDate);
    }
    if (patch.nextDueMileage !== undefined) {
      fields.push(`next_due_mileage = $${i++}`);
      params.push(patch.nextDueMileage);
    }
    if (patch.isActive !== undefined) {
      fields.push(`is_active = $${i++}`);
      params.push(patch.isActive);
    }
    if (patch.emailNotifications !== undefined) {
      fields.push(`email_notifications = $${i++}`);
      params.push(patch.emailNotifications);
    }
    if (patch.scheduleType !== undefined) {
      fields.push(`schedule_type = $${i++}`);
      params.push(patch.scheduleType);
    }
    if (patch.fixedDueDate !== undefined) {
      fields.push(`fixed_due_date = $${i++}`);
      params.push(patch.fixedDueDate);
    }
    if (patch.reminderDays1 !== undefined) {
      fields.push(`reminder_days_1 = $${i++}`);
      params.push(patch.reminderDays1);
    }
    if (patch.reminderDays2 !== undefined) {
      fields.push(`reminder_days_2 = $${i++}`);
      params.push(patch.reminderDays2);
    }
    if (patch.reminderDays3 !== undefined) {
      fields.push(`reminder_days_3 = $${i++}`);
      params.push(patch.reminderDays3);
    }

    if (!fields.length) return this.findScheduleById(id, userId);

    params.push(id, userId);
    const sql = `UPDATE maintenance_schedules SET ${fields.join(', ')} WHERE id = $${i++} AND user_id = $${i++} RETURNING *`;
    const res = await this.db.query(sql, params);
    return res.rows[0] ? this.mapMaintenanceSchedule(res.rows[0]) : null;
  }

  async batchInsertSchedules(
    schedules: Array<{
      id: string;
      userId: string;
      vehicleId: string;
      category: MaintenanceCategory;
      subtypes: string[];
      intervalMonths?: number | null;
      intervalMiles?: number | null;
      lastServiceDate?: string | null;
      lastServiceMileage?: number | null;
      nextDueDate?: string | null;
      nextDueMileage?: number | null;
      isActive: boolean;
      emailNotifications?: boolean;
      scheduleType?: string;
      fixedDueDate?: string | null;
      reminderDays1?: number | null;
      reminderDays2?: number | null;
      reminderDays3?: number | null;
    }>,
    client?: any
  ): Promise<MaintenanceSchedule[]> {
    if (schedules.length === 0) {
      return [];
    }

    // Multi-value INSERT for performance (avoids N round-trips)
    const queryClient = client || this.db;
    const placeholders: string[] = [];
    const values: any[] = [];
    let paramCount = 1;

    schedules.forEach((schedule) => {
      const scheduleParams = [
        schedule.id,
        schedule.userId,
        schedule.vehicleId,
        schedule.category,
        schedule.subtypes,
        schedule.intervalMonths ?? null,
        schedule.intervalMiles ?? null,
        schedule.lastServiceDate ?? null,
        schedule.lastServiceMileage ?? null,
        schedule.nextDueDate ?? null,
        schedule.nextDueMileage ?? null,
        schedule.isActive,
        schedule.emailNotifications ?? false,
        schedule.scheduleType ?? 'interval',
        schedule.fixedDueDate ?? null,
        schedule.reminderDays1 ?? null,
        schedule.reminderDays2 ?? null,
        schedule.reminderDays3 ?? null
      ];

      const placeholder = `($${paramCount++}, $${paramCount++}, $${paramCount++}, $${paramCount++}, $${paramCount++}::text[], $${paramCount++}, $${paramCount++}, $${paramCount++}, $${paramCount++}, $${paramCount++}, $${paramCount++}, $${paramCount++}, $${paramCount++}, $${paramCount++}, $${paramCount++}, $${paramCount++}, $${paramCount++}, $${paramCount++})`;
      placeholders.push(placeholder);
      values.push(...scheduleParams);
    });

    const query = `
      INSERT INTO maintenance_schedules (
        id, user_id, vehicle_id, category, subtypes, interval_months, interval_miles,
        last_service_date, last_service_mileage, next_due_date, next_due_mileage, is_active, email_notifications,
        schedule_type, fixed_due_date, reminder_days_1, reminder_days_2, reminder_days_3
      )
      VALUES ${placeholders.join(', ')}
      RETURNING *
    `;

    const result = await queryClient.query(query, values);
    return result.rows.map((row: any) => this.mapMaintenanceSchedule(row));
  }

  async deleteSchedule(id: string, userId: string): Promise<void> {
    await this.db.query(
      `DELETE FROM maintenance_schedules WHERE id = $1 AND user_id = $2`,
      [id, userId]
    );
  }

  async findMatchingSchedules(
    userId: string,
    vehicleId: string,
    category: MaintenanceCategory,
    subtypes: string[]
  ): Promise<MaintenanceSchedule[]> {
    const result = await this.db.query(
      `SELECT * FROM maintenance_schedules
       WHERE user_id = $1
         AND vehicle_id = $2
         AND category = $3
         AND is_active = true
         AND schedule_type = 'time_since_last'
         AND subtypes && $4::text[]
       ORDER BY created_at DESC`,
      [userId, vehicleId, category, subtypes]
    );
    return result.rows.map(row => this.mapMaintenanceSchedule(row));
  }

  async updateScheduleLastService(
    id: string,
    userId: string,
    lastServiceDate: string,
    lastServiceMileage?: number | null,
    nextDueDate?: string | null,
    nextDueMileage?: number | null
  ): Promise<MaintenanceSchedule | null> {
    const result = await this.db.query(
      `UPDATE maintenance_schedules SET
         last_service_date = $1,
         last_service_mileage = $2,
         next_due_date = $3,
         next_due_mileage = $4,
         updated_at = CURRENT_TIMESTAMP
       WHERE id = $5 AND user_id = $6
       RETURNING *`,
      [lastServiceDate, lastServiceMileage, nextDueDate, nextDueMileage, id, userId]
    );
    return result.rows[0] ? this.mapMaintenanceSchedule(result.rows[0]) : null;
  }

  async findSchedulesByUserId(userId: string): Promise<MaintenanceSchedule[]> {
    const res = await this.db.query(
      `SELECT * FROM maintenance_schedules WHERE user_id = $1 ORDER BY created_at DESC`,
      [userId]
    );
    return res.rows.map(row => this.mapMaintenanceSchedule(row));
  }
}
