/**
 * @ai-summary Vehicle catalog management service (normalized schema)
 * @ai-context Performs CRUD against vehicles.make/model/model_year/trim/engine tables
 */

import { Pool, PoolClient } from 'pg';
import { logger } from '../../../core/logging/logger';
import { PlatformCacheService } from '../../platform/domain/platform-cache.service';

export interface CatalogMake {
  id: number;
  name: string;
  createdAt: string;
  updatedAt: string;
}

export interface CatalogModel {
  id: number;
  makeId: number;
  name: string;
  createdAt: string;
  updatedAt: string;
}

export interface CatalogYear {
  id: number;
  modelId: number;
  year: number;
  createdAt: string;
  updatedAt: string;
}

export interface CatalogTrim {
  id: number;
  yearId: number;
  name: string;
  createdAt: string;
  updatedAt: string;
}

export interface CatalogEngine {
  id: number;
  trimId: number;
  name: string;
  displacement?: string | null;
  cylinders?: number | null;
  fuel_type?: string | null;
  createdAt: string;
  updatedAt: string;
}

export interface PlatformChangeLog {
  id: string;
  changeType: 'CREATE' | 'UPDATE' | 'DELETE';
  resourceType: 'makes' | 'models' | 'years' | 'trims' | 'engines';
  resourceId: string;
  oldValue: any;
  newValue: any;
  changedBy: string;
  createdAt: Date;
}

export interface CatalogSearchResult {
  id: number;
  year: number;
  make: string;
  model: string;
  trim: string;
  engineId: number | null;
  engineName: string | null;
  transmissionId: number | null;
  transmissionType: string | null;
}

export interface CatalogSearchResponse {
  items: CatalogSearchResult[];
  total: number;
  page: number;
  pageSize: number;
}

export interface CascadeDeleteResult {
  deletedMakes: number;
  deletedModels: number;
  deletedYears: number;
  deletedTrims: number;
  deletedEngines: number;
  totalDeleted: number;
}

const VEHICLE_SCHEMA = 'vehicles';

export class VehicleCatalogService {
  constructor(
    private pool: Pool,
    private cacheService: PlatformCacheService
  ) {}

  // MAKES ------------------------------------------------------------------

  async getAllMakes(): Promise<CatalogMake[]> {
    const query = `
      SELECT id, name, created_at, updated_at
      FROM ${VEHICLE_SCHEMA}.make
      ORDER BY name
    `;

    const result = await this.pool.query(query);
    return result.rows.map((row) => this.mapMakeRow(row));
  }

  async createMake(name: string, changedBy: string): Promise<CatalogMake> {
    const make = await this.runInTransaction(async (client) => {
      const result = await client.query(
        `
          INSERT INTO ${VEHICLE_SCHEMA}.make (name)
          VALUES ($1)
          RETURNING id, name, created_at, updated_at
        `,
        [name.trim()]
      );

      const make = this.mapMakeRow(result.rows[0]);
      await this.logChange(client, 'CREATE', 'makes', make.id.toString(), null, make, changedBy);
      return make;
    });
    await this.cacheService.invalidateVehicleData();
    return make;
  }

  async updateMake(makeId: number, name: string, changedBy: string): Promise<CatalogMake> {
    const value = await this.runInTransaction(async (client) => {
      const existing = await client.query(
        `SELECT id, name, created_at, updated_at FROM ${VEHICLE_SCHEMA}.make WHERE id = $1`,
        [makeId]
      );
      if (existing.rowCount === 0) {
        throw new Error(`Make ${makeId} not found`);
      }

      const oldValue = this.mapMakeRow(existing.rows[0]);
      const updated = await client.query(
        `
          UPDATE ${VEHICLE_SCHEMA}.make
          SET name = $2, updated_at = NOW()
          WHERE id = $1
          RETURNING id, name, created_at, updated_at
        `,
        [makeId, name.trim()]
      );

      const newValue = this.mapMakeRow(updated.rows[0]);
      await this.logChange(client, 'UPDATE', 'makes', makeId.toString(), oldValue, newValue, changedBy);
      return newValue;
    });
    await this.cacheService.invalidateVehicleData();
    return value;
  }

  async deleteMake(makeId: number, changedBy: string): Promise<void> {
    await this.runInTransaction(async (client) => {
      const dependency = await client.query(
        `SELECT 1 FROM ${VEHICLE_SCHEMA}.model WHERE make_id = $1 LIMIT 1`,
        [makeId]
      );
      if ((dependency.rowCount || 0) > 0) {
        throw new Error('Cannot delete make with existing models');
      }

      const existing = await client.query(
        `SELECT id, name, created_at, updated_at FROM ${VEHICLE_SCHEMA}.make WHERE id = $1`,
        [makeId]
      );
      if (existing.rowCount === 0) {
        throw new Error(`Make ${makeId} not found`);
      }

      const oldValue = this.mapMakeRow(existing.rows[0]);
      await client.query(`DELETE FROM ${VEHICLE_SCHEMA}.make WHERE id = $1`, [makeId]);
      await this.logChange(client, 'DELETE', 'makes', makeId.toString(), oldValue, null, changedBy);
    });
    await this.cacheService.invalidateVehicleData();
  }

  // MODELS -----------------------------------------------------------------

  async getModelsByMake(makeId: number): Promise<CatalogModel[]> {
    const result = await this.pool.query(
      `
        SELECT id, make_id, name, created_at, updated_at
        FROM ${VEHICLE_SCHEMA}.model
        WHERE make_id = $1
        ORDER BY name
      `,
      [makeId]
    );
    return result.rows.map((row) => this.mapModelRow(row));
  }

  async createModel(makeId: number, name: string, changedBy: string): Promise<CatalogModel> {
    const model = await this.runInTransaction(async (client) => {
      await this.assertMakeExists(client, makeId);

      const result = await client.query(
        `
          INSERT INTO ${VEHICLE_SCHEMA}.model (make_id, name)
          VALUES ($1, $2)
          RETURNING id, make_id, name, created_at, updated_at
        `,
        [makeId, name.trim()]
      );

      const model = this.mapModelRow(result.rows[0]);
      await this.logChange(client, 'CREATE', 'models', model.id.toString(), null, model, changedBy);
      return model;
    });
    await this.cacheService.invalidateVehicleData();
    return model;
  }

  async updateModel(modelId: number, makeId: number, name: string, changedBy: string): Promise<CatalogModel> {
    const value = await this.runInTransaction(async (client) => {
      await this.assertMakeExists(client, makeId);

      const existing = await client.query(
        `SELECT id, make_id, name, created_at, updated_at FROM ${VEHICLE_SCHEMA}.model WHERE id = $1`,
        [modelId]
      );
      if (existing.rowCount === 0) {
        throw new Error(`Model ${modelId} not found`);
      }
      const oldValue = this.mapModelRow(existing.rows[0]);

      const result = await client.query(
        `
          UPDATE ${VEHICLE_SCHEMA}.model
          SET make_id = $2, name = $3, updated_at = NOW()
          WHERE id = $1
          RETURNING id, make_id, name, created_at, updated_at
        `,
        [modelId, makeId, name.trim()]
      );

      const newValue = this.mapModelRow(result.rows[0]);
      await this.logChange(client, 'UPDATE', 'models', modelId.toString(), oldValue, newValue, changedBy);
      return newValue;
    });
    await this.cacheService.invalidateVehicleData();
    return value;
  }

  async deleteModel(modelId: number, changedBy: string): Promise<void> {
    await this.runInTransaction(async (client) => {
      const dependency = await client.query(
        `SELECT 1 FROM ${VEHICLE_SCHEMA}.model_year WHERE model_id = $1 LIMIT 1`,
        [modelId]
      );
      if ((dependency.rowCount || 0) > 0) {
        throw new Error('Cannot delete model with existing years');
      }

      const existing = await client.query(
        `SELECT id, make_id, name, created_at, updated_at FROM ${VEHICLE_SCHEMA}.model WHERE id = $1`,
        [modelId]
      );
      if (existing.rowCount === 0) {
        throw new Error(`Model ${modelId} not found`);
      }

      const oldValue = this.mapModelRow(existing.rows[0]);
      await client.query(`DELETE FROM ${VEHICLE_SCHEMA}.model WHERE id = $1`, [modelId]);
      await this.logChange(client, 'DELETE', 'models', modelId.toString(), oldValue, null, changedBy);
    });
    await this.cacheService.invalidateVehicleData();
  }

  // YEARS ------------------------------------------------------------------

  async getYearsByModel(modelId: number): Promise<CatalogYear[]> {
    const result = await this.pool.query(
      `
        SELECT id, model_id, year, created_at, updated_at
        FROM ${VEHICLE_SCHEMA}.model_year
        WHERE model_id = $1
        ORDER BY year
      `,
      [modelId]
    );
    return result.rows.map((row) => this.mapYearRow(row));
  }

  async createYear(modelId: number, year: number, changedBy: string): Promise<CatalogYear> {
    const value = await this.runInTransaction(async (client) => {
      await this.assertModelExists(client, modelId);

      const result = await client.query(
        `
          INSERT INTO ${VEHICLE_SCHEMA}.model_year (model_id, year)
          VALUES ($1, $2)
          RETURNING id, model_id, year, created_at, updated_at
        `,
        [modelId, year]
      );

      const value = this.mapYearRow(result.rows[0]);
      await this.logChange(client, 'CREATE', 'years', value.id.toString(), null, value, changedBy);
      return value;
    });
    await this.cacheService.invalidateVehicleData();
    return value;
  }

  async updateYear(yearId: number, modelId: number, year: number, changedBy: string): Promise<CatalogYear> {
    const value = await this.runInTransaction(async (client) => {
      await this.assertModelExists(client, modelId);

      const existing = await client.query(
        `SELECT id, model_id, year, created_at, updated_at FROM ${VEHICLE_SCHEMA}.model_year WHERE id = $1`,
        [yearId]
      );
      if (existing.rowCount === 0) {
        throw new Error(`Year ${yearId} not found`);
      }
      const oldValue = this.mapYearRow(existing.rows[0]);

      const result = await client.query(
        `
          UPDATE ${VEHICLE_SCHEMA}.model_year
          SET model_id = $2, year = $3, updated_at = NOW()
          WHERE id = $1
          RETURNING id, model_id, year, created_at, updated_at
        `,
        [yearId, modelId, year]
      );

      const newValue = this.mapYearRow(result.rows[0]);
      await this.logChange(client, 'UPDATE', 'years', yearId.toString(), oldValue, newValue, changedBy);
      return newValue;
    });
    await this.cacheService.invalidateVehicleData();
    return value;
  }

  async deleteYear(yearId: number, changedBy: string): Promise<void> {
    await this.runInTransaction(async (client) => {
      const dependency = await client.query(
        `SELECT 1 FROM ${VEHICLE_SCHEMA}.trim WHERE model_year_id = $1 LIMIT 1`,
        [yearId]
      );
      if ((dependency.rowCount || 0) > 0) {
        throw new Error('Cannot delete year with existing trims');
      }

      const existing = await client.query(
        `SELECT id, model_id, year, created_at, updated_at FROM ${VEHICLE_SCHEMA}.model_year WHERE id = $1`,
        [yearId]
      );
      if (existing.rowCount === 0) {
        throw new Error(`Year ${yearId} not found`);
      }

      const oldValue = this.mapYearRow(existing.rows[0]);
      await client.query(`DELETE FROM ${VEHICLE_SCHEMA}.model_year WHERE id = $1`, [yearId]);
      await this.logChange(client, 'DELETE', 'years', yearId.toString(), oldValue, null, changedBy);
    });
    await this.cacheService.invalidateVehicleData();
  }

  // TRIMS ------------------------------------------------------------------

  async getTrimsByYear(yearId: number): Promise<CatalogTrim[]> {
    const result = await this.pool.query(
      `
        SELECT id, model_year_id, name, created_at, updated_at
        FROM ${VEHICLE_SCHEMA}.trim
        WHERE model_year_id = $1
        ORDER BY name
      `,
      [yearId]
    );
    return result.rows.map((row) => this.mapTrimRow(row));
  }

  async createTrim(yearId: number, name: string, changedBy: string): Promise<CatalogTrim> {
    const value = await this.runInTransaction(async (client) => {
      await this.assertYearExists(client, yearId);

      const result = await client.query(
        `
          INSERT INTO ${VEHICLE_SCHEMA}.trim (model_year_id, name)
          VALUES ($1, $2)
          RETURNING id, model_year_id, name, created_at, updated_at
        `,
        [yearId, name.trim()]
      );

      const value = this.mapTrimRow(result.rows[0]);
      await this.logChange(client, 'CREATE', 'trims', value.id.toString(), null, value, changedBy);
      return value;
    });
    await this.cacheService.invalidateVehicleData();
    return value;
  }

  async updateTrim(trimId: number, yearId: number, name: string, changedBy: string): Promise<CatalogTrim> {
    const value = await this.runInTransaction(async (client) => {
      await this.assertYearExists(client, yearId);

      const existing = await client.query(
        `SELECT id, model_year_id, name, created_at, updated_at FROM ${VEHICLE_SCHEMA}.trim WHERE id = $1`,
        [trimId]
      );
      if (existing.rowCount === 0) {
        throw new Error(`Trim ${trimId} not found`);
      }
      const oldValue = this.mapTrimRow(existing.rows[0]);

      const result = await client.query(
        `
          UPDATE ${VEHICLE_SCHEMA}.trim
          SET model_year_id = $2, name = $3, updated_at = NOW()
          WHERE id = $1
          RETURNING id, model_year_id, name, created_at, updated_at
        `,
        [trimId, yearId, name.trim()]
      );

      const newValue = this.mapTrimRow(result.rows[0]);
      await this.logChange(client, 'UPDATE', 'trims', trimId.toString(), oldValue, newValue, changedBy);
      return newValue;
    });
    await this.cacheService.invalidateVehicleData();
    return value;
  }

  async deleteTrim(trimId: number, changedBy: string): Promise<void> {
    await this.runInTransaction(async (client) => {
      const dependency = await client.query(
        `SELECT 1 FROM ${VEHICLE_SCHEMA}.trim_engine WHERE trim_id = $1 LIMIT 1`,
        [trimId]
      );
      if ((dependency.rowCount || 0) > 0) {
        throw new Error('Cannot delete trim with existing engines');
      }

      const existing = await client.query(
        `SELECT id, model_year_id, name, created_at, updated_at FROM ${VEHICLE_SCHEMA}.trim WHERE id = $1`,
        [trimId]
      );
      if (existing.rowCount === 0) {
        throw new Error(`Trim ${trimId} not found`);
      }

      const oldValue = this.mapTrimRow(existing.rows[0]);
      await client.query(`DELETE FROM ${VEHICLE_SCHEMA}.trim WHERE id = $1`, [trimId]);
      await this.logChange(client, 'DELETE', 'trims', trimId.toString(), oldValue, null, changedBy);
    });
    await this.cacheService.invalidateVehicleData();
  }

  // ENGINES ----------------------------------------------------------------

  async getEnginesByTrim(trimId: number): Promise<CatalogEngine[]> {
    const result = await this.pool.query(
      `
        SELECT e.id,
               e.name,
               e.displacement_l,
               e.cylinders,
               e.fuel_type,
               e.created_at,
               e.updated_at,
               te.trim_id
        FROM ${VEHICLE_SCHEMA}.engine e
        JOIN ${VEHICLE_SCHEMA}.trim_engine te ON te.engine_id = e.id
        WHERE te.trim_id = $1
        ORDER BY e.name
      `,
      [trimId]
    );

    return result.rows.map((row) => this.mapEngineRow(row));
  }

  async createEngine(
    trimId: number,
    name: string,
    _description: string | undefined,
    changedBy: string
  ): Promise<CatalogEngine> {
    const value = await this.runInTransaction(async (client) => {
      await this.assertTrimExists(client, trimId);

      const engineResult = await client.query(
        `
          INSERT INTO ${VEHICLE_SCHEMA}.engine (name)
          VALUES ($1)
          RETURNING id, name, displacement_l, cylinders, fuel_type, created_at, updated_at
        `,
        [name.trim()]
      );

      const engineId = engineResult.rows[0].id;
      await client.query(
        `
          INSERT INTO ${VEHICLE_SCHEMA}.trim_engine (trim_id, engine_id)
          VALUES ($1, $2)
          ON CONFLICT DO NOTHING
        `,
        [trimId, engineId]
      );

      const value = this.mapEngineRow({ ...engineResult.rows[0], trim_id: trimId });
      await this.logChange(client, 'CREATE', 'engines', engineId.toString(), null, value, changedBy);
      return value;
    });
    await this.cacheService.invalidateVehicleData();
    return value;
  }

  async updateEngine(
    engineId: number,
    trimId: number,
    name: string,
    _description: string | undefined,
    changedBy: string
  ): Promise<CatalogEngine> {
    const value = await this.runInTransaction(async (client) => {
      await this.assertTrimExists(client, trimId);

      const existing = await client.query(
        `
          SELECT e.id,
                 e.name,
                 e.displacement_l,
                 e.cylinders,
                 e.fuel_type,
                 e.created_at,
                 e.updated_at,
                 te.trim_id
          FROM ${VEHICLE_SCHEMA}.engine e
          JOIN ${VEHICLE_SCHEMA}.trim_engine te ON te.engine_id = e.id
          WHERE e.id = $1 AND te.trim_id = $2
        `,
        [engineId, trimId]
      );

      if (existing.rowCount === 0) {
        throw new Error(`Engine ${engineId} not found for trim ${trimId}`);
      }

      const oldValue = this.mapEngineRow(existing.rows[0]);
      const updated = await client.query(
        `
          UPDATE ${VEHICLE_SCHEMA}.engine
          SET name = $2, updated_at = NOW()
          WHERE id = $1
          RETURNING id, name, displacement_l, cylinders, fuel_type, created_at, updated_at
        `,
        [engineId, name.trim()]
      );

      const newValue = this.mapEngineRow({ ...updated.rows[0], trim_id: trimId });
      await this.logChange(client, 'UPDATE', 'engines', engineId.toString(), oldValue, newValue, changedBy);
      return newValue;
    });
    await this.cacheService.invalidateVehicleData();
    return value;
  }

  async deleteEngine(engineId: number, changedBy: string): Promise<void> {
    await this.runInTransaction(async (client) => {
      const existing = await client.query(
        `
          SELECT e.id,
                 e.name,
                 e.displacement_l,
                 e.cylinders,
                 e.fuel_type,
                 e.created_at,
                 e.updated_at,
                 te.trim_id
          FROM ${VEHICLE_SCHEMA}.engine e
          JOIN ${VEHICLE_SCHEMA}.trim_engine te ON te.engine_id = e.id
          WHERE e.id = $1
        `,
        [engineId]
      );

      if (existing.rowCount === 0) {
        throw new Error(`Engine ${engineId} not found`);
      }

      const oldValue = this.mapEngineRow(existing.rows[0]);
      await client.query(
        `DELETE FROM ${VEHICLE_SCHEMA}.trim_engine WHERE engine_id = $1`,
        [engineId]
      );
      await client.query(`DELETE FROM ${VEHICLE_SCHEMA}.engine WHERE id = $1`, [engineId]);

      await this.logChange(client, 'DELETE', 'engines', engineId.toString(), oldValue, null, changedBy);
    });
    await this.cacheService.invalidateVehicleData();
  }

  // CHANGE LOGS ------------------------------------------------------------

  async getChangeLogs(limit: number = 100, offset: number = 0): Promise<{ logs: PlatformChangeLog[]; total: number }> {
    const countQuery = 'SELECT COUNT(*) as total FROM platform_change_log';
    const query = `
      SELECT id, change_type, resource_type, resource_id, old_value, new_value, changed_by, created_at
      FROM platform_change_log
      ORDER BY created_at DESC
      LIMIT $1 OFFSET $2
    `;

    try {
      const [countResult, dataResult] = await Promise.all([
        this.pool.query(countQuery),
        this.pool.query(query, [limit, offset])
      ]);

      const total = parseInt(countResult.rows[0].total, 10);
      const logs = dataResult.rows.map((row) => ({
        id: row.id,
        changeType: row.change_type,
        resourceType: row.resource_type,
        resourceId: row.resource_id,
        oldValue: row.old_value,
        newValue: row.new_value,
        changedBy: row.changed_by,
        createdAt: new Date(row.created_at),
      }));

      return { logs, total };
    } catch (error) {
      logger.error('Error fetching change logs', { error });
      throw error;
    }
  }

  // SEARCH -----------------------------------------------------------------

  async searchCatalog(
    query: string,
    page: number = 1,
    pageSize: number = 50
  ): Promise<CatalogSearchResponse> {
    const offset = (page - 1) * pageSize;
    const sanitizedQuery = query.trim();

    if (!sanitizedQuery) {
      return { items: [], total: 0, page, pageSize };
    }

    // Convert query to tsquery format - split words and join with &
    const tsQueryTerms = sanitizedQuery
      .split(/\s+/)
      .filter(term => term.length > 0)
      .map(term => term.replace(/[^\w]/g, ''))
      .filter(term => term.length > 0)
      .map(term => `${term}:*`)
      .join(' & ');

    if (!tsQueryTerms) {
      return { items: [], total: 0, page, pageSize };
    }

    try {
      // Count total matching records
      const countQuery = `
        SELECT COUNT(*) as total
        FROM vehicle_options vo
        WHERE to_tsvector('english', vo.year::text || ' ' || vo.make || ' ' || vo.model || ' ' || vo.trim)
              @@ to_tsquery('english', $1)
      `;
      const countResult = await this.pool.query(countQuery, [tsQueryTerms]);
      const total = parseInt(countResult.rows[0].total, 10);

      // Fetch paginated results with engine and transmission details
      const searchQuery = `
        SELECT
          vo.id,
          vo.year,
          vo.make,
          vo.model,
          vo.trim,
          vo.engine_id,
          e.name AS engine_name,
          vo.transmission_id,
          t.type AS transmission_type
        FROM vehicle_options vo
        LEFT JOIN engines e ON vo.engine_id = e.id
        LEFT JOIN transmissions t ON vo.transmission_id = t.id
        WHERE to_tsvector('english', vo.year::text || ' ' || vo.make || ' ' || vo.model || ' ' || vo.trim)
              @@ to_tsquery('english', $1)
        ORDER BY vo.year DESC, vo.make ASC, vo.model ASC, vo.trim ASC
        LIMIT $2 OFFSET $3
      `;

      const result = await this.pool.query(searchQuery, [tsQueryTerms, pageSize, offset]);

      const items: CatalogSearchResult[] = result.rows.map((row) => ({
        id: Number(row.id),
        year: Number(row.year),
        make: row.make,
        model: row.model,
        trim: row.trim,
        engineId: row.engine_id ? Number(row.engine_id) : null,
        engineName: row.engine_name || null,
        transmissionId: row.transmission_id ? Number(row.transmission_id) : null,
        transmissionType: row.transmission_type || null,
      }));

      return { items, total, page, pageSize };
    } catch (error) {
      logger.error('Error searching catalog', { error, query: sanitizedQuery });
      throw error;
    }
  }

  // CASCADE DELETE METHODS -------------------------------------------------

  async deleteMakeCascade(makeId: number, changedBy: string): Promise<CascadeDeleteResult> {
    const result: CascadeDeleteResult = {
      deletedMakes: 0,
      deletedModels: 0,
      deletedYears: 0,
      deletedTrims: 0,
      deletedEngines: 0,
      totalDeleted: 0,
    };

    await this.runInTransaction(async (client) => {
      // Verify make exists
      const makeResult = await client.query(
        `SELECT id, name, created_at, updated_at FROM ${VEHICLE_SCHEMA}.make WHERE id = $1`,
        [makeId]
      );
      if (makeResult.rowCount === 0) {
        throw new Error(`Make ${makeId} not found`);
      }
      const makeData = this.mapMakeRow(makeResult.rows[0]);

      // Get all models for this make
      const modelsResult = await client.query(
        `SELECT id FROM ${VEHICLE_SCHEMA}.model WHERE make_id = $1`,
        [makeId]
      );

      // Cascade delete all models and their children
      for (const modelRow of modelsResult.rows) {
        const modelDeletes = await this.deleteModelCascadeInTransaction(client, modelRow.id, changedBy);
        result.deletedModels += modelDeletes.deletedModels;
        result.deletedYears += modelDeletes.deletedYears;
        result.deletedTrims += modelDeletes.deletedTrims;
        result.deletedEngines += modelDeletes.deletedEngines;
      }

      // Delete the make itself
      await client.query(`DELETE FROM ${VEHICLE_SCHEMA}.make WHERE id = $1`, [makeId]);
      await this.logChange(client, 'DELETE', 'makes', makeId.toString(), makeData, null, changedBy);
      result.deletedMakes = 1;
    });

    result.totalDeleted = result.deletedMakes + result.deletedModels + result.deletedYears + result.deletedTrims + result.deletedEngines;
    await this.cacheService.invalidateVehicleData();
    return result;
  }

  async deleteModelCascade(modelId: number, changedBy: string): Promise<CascadeDeleteResult> {
    const result: CascadeDeleteResult = {
      deletedMakes: 0,
      deletedModels: 0,
      deletedYears: 0,
      deletedTrims: 0,
      deletedEngines: 0,
      totalDeleted: 0,
    };

    await this.runInTransaction(async (client) => {
      const deletes = await this.deleteModelCascadeInTransaction(client, modelId, changedBy);
      result.deletedModels = deletes.deletedModels;
      result.deletedYears = deletes.deletedYears;
      result.deletedTrims = deletes.deletedTrims;
      result.deletedEngines = deletes.deletedEngines;
    });

    result.totalDeleted = result.deletedModels + result.deletedYears + result.deletedTrims + result.deletedEngines;
    await this.cacheService.invalidateVehicleData();
    return result;
  }

  async deleteYearCascade(yearId: number, changedBy: string): Promise<CascadeDeleteResult> {
    const result: CascadeDeleteResult = {
      deletedMakes: 0,
      deletedModels: 0,
      deletedYears: 0,
      deletedTrims: 0,
      deletedEngines: 0,
      totalDeleted: 0,
    };

    await this.runInTransaction(async (client) => {
      const deletes = await this.deleteYearCascadeInTransaction(client, yearId, changedBy);
      result.deletedYears = deletes.deletedYears;
      result.deletedTrims = deletes.deletedTrims;
      result.deletedEngines = deletes.deletedEngines;
    });

    result.totalDeleted = result.deletedYears + result.deletedTrims + result.deletedEngines;
    await this.cacheService.invalidateVehicleData();
    return result;
  }

  async deleteTrimCascade(trimId: number, changedBy: string): Promise<CascadeDeleteResult> {
    const result: CascadeDeleteResult = {
      deletedMakes: 0,
      deletedModels: 0,
      deletedYears: 0,
      deletedTrims: 0,
      deletedEngines: 0,
      totalDeleted: 0,
    };

    await this.runInTransaction(async (client) => {
      const deletes = await this.deleteTrimCascadeInTransaction(client, trimId, changedBy);
      result.deletedTrims = deletes.deletedTrims;
      result.deletedEngines = deletes.deletedEngines;
    });

    result.totalDeleted = result.deletedTrims + result.deletedEngines;
    await this.cacheService.invalidateVehicleData();
    return result;
  }

  // Private cascade helpers (run within existing transaction)

  private async deleteModelCascadeInTransaction(
    client: PoolClient,
    modelId: number,
    changedBy: string
  ): Promise<{ deletedModels: number; deletedYears: number; deletedTrims: number; deletedEngines: number }> {
    const result = { deletedModels: 0, deletedYears: 0, deletedTrims: 0, deletedEngines: 0 };

    // Verify model exists
    const modelResult = await client.query(
      `SELECT id, make_id, name, created_at, updated_at FROM ${VEHICLE_SCHEMA}.model WHERE id = $1`,
      [modelId]
    );
    if (modelResult.rowCount === 0) {
      throw new Error(`Model ${modelId} not found`);
    }
    const modelData = this.mapModelRow(modelResult.rows[0]);

    // Get all years for this model
    const yearsResult = await client.query(
      `SELECT id FROM ${VEHICLE_SCHEMA}.model_year WHERE model_id = $1`,
      [modelId]
    );

    // Cascade delete all years and their children
    for (const yearRow of yearsResult.rows) {
      const yearDeletes = await this.deleteYearCascadeInTransaction(client, yearRow.id, changedBy);
      result.deletedYears += yearDeletes.deletedYears;
      result.deletedTrims += yearDeletes.deletedTrims;
      result.deletedEngines += yearDeletes.deletedEngines;
    }

    // Delete the model itself
    await client.query(`DELETE FROM ${VEHICLE_SCHEMA}.model WHERE id = $1`, [modelId]);
    await this.logChange(client, 'DELETE', 'models', modelId.toString(), modelData, null, changedBy);
    result.deletedModels = 1;

    return result;
  }

  private async deleteYearCascadeInTransaction(
    client: PoolClient,
    yearId: number,
    changedBy: string
  ): Promise<{ deletedYears: number; deletedTrims: number; deletedEngines: number }> {
    const result = { deletedYears: 0, deletedTrims: 0, deletedEngines: 0 };

    // Verify year exists
    const yearResult = await client.query(
      `SELECT id, model_id, year, created_at, updated_at FROM ${VEHICLE_SCHEMA}.model_year WHERE id = $1`,
      [yearId]
    );
    if (yearResult.rowCount === 0) {
      throw new Error(`Year ${yearId} not found`);
    }
    const yearData = this.mapYearRow(yearResult.rows[0]);

    // Get all trims for this year
    const trimsResult = await client.query(
      `SELECT id FROM ${VEHICLE_SCHEMA}.trim WHERE model_year_id = $1`,
      [yearId]
    );

    // Cascade delete all trims and their engines
    for (const trimRow of trimsResult.rows) {
      const trimDeletes = await this.deleteTrimCascadeInTransaction(client, trimRow.id, changedBy);
      result.deletedTrims += trimDeletes.deletedTrims;
      result.deletedEngines += trimDeletes.deletedEngines;
    }

    // Delete the year itself
    await client.query(`DELETE FROM ${VEHICLE_SCHEMA}.model_year WHERE id = $1`, [yearId]);
    await this.logChange(client, 'DELETE', 'years', yearId.toString(), yearData, null, changedBy);
    result.deletedYears = 1;

    return result;
  }

  private async deleteTrimCascadeInTransaction(
    client: PoolClient,
    trimId: number,
    changedBy: string
  ): Promise<{ deletedTrims: number; deletedEngines: number }> {
    const result = { deletedTrims: 0, deletedEngines: 0 };

    // Verify trim exists
    const trimResult = await client.query(
      `SELECT id, model_year_id, name, created_at, updated_at FROM ${VEHICLE_SCHEMA}.trim WHERE id = $1`,
      [trimId]
    );
    if (trimResult.rowCount === 0) {
      throw new Error(`Trim ${trimId} not found`);
    }
    const trimData = this.mapTrimRow(trimResult.rows[0]);

    // Get all engines linked to this trim
    const enginesResult = await client.query(
      `SELECT e.id, e.name, e.displacement_l, e.cylinders, e.fuel_type, e.created_at, e.updated_at, te.trim_id
       FROM ${VEHICLE_SCHEMA}.engine e
       JOIN ${VEHICLE_SCHEMA}.trim_engine te ON te.engine_id = e.id
       WHERE te.trim_id = $1`,
      [trimId]
    );

    // Delete engine links and potentially orphaned engines
    for (const engineRow of enginesResult.rows) {
      const engineData = this.mapEngineRow(engineRow);

      // Remove the link first
      await client.query(
        `DELETE FROM ${VEHICLE_SCHEMA}.trim_engine WHERE trim_id = $1 AND engine_id = $2`,
        [trimId, engineRow.id]
      );

      // Check if this engine is used by other trims
      const otherLinksResult = await client.query(
        `SELECT 1 FROM ${VEHICLE_SCHEMA}.trim_engine WHERE engine_id = $1 LIMIT 1`,
        [engineRow.id]
      );

      // If no other trims use this engine, delete the engine itself
      if ((otherLinksResult.rowCount || 0) === 0) {
        await client.query(`DELETE FROM ${VEHICLE_SCHEMA}.engine WHERE id = $1`, [engineRow.id]);
        await this.logChange(client, 'DELETE', 'engines', engineRow.id.toString(), engineData, null, changedBy);
        result.deletedEngines += 1;
      }
    }

    // Delete the trim itself
    await client.query(`DELETE FROM ${VEHICLE_SCHEMA}.trim WHERE id = $1`, [trimId]);
    await this.logChange(client, 'DELETE', 'trims', trimId.toString(), trimData, null, changedBy);
    result.deletedTrims = 1;

    return result;
  }

  // HELPERS ----------------------------------------------------------------

  private async runInTransaction<T>(handler: (client: PoolClient) => Promise<T>): Promise<T> {
    const client = await this.pool.connect();
    try {
      await client.query('BEGIN');
      const result = await handler(client);
      await client.query('COMMIT');
      return result;
    } catch (error) {
      await client.query('ROLLBACK');
      throw error;
    } finally {
      client.release();
    }
  }

  private mapMakeRow(row: any): CatalogMake {
    return {
      id: Number(row.id),
      name: row.name,
      createdAt: this.toIsoDate(row.created_at),
      updatedAt: this.toIsoDate(row.updated_at),
    };
  }

  private mapModelRow(row: any): CatalogModel {
    return {
      id: Number(row.id),
      makeId: Number(row.make_id),
      name: row.name,
      createdAt: this.toIsoDate(row.created_at),
      updatedAt: this.toIsoDate(row.updated_at),
    };
  }

  private mapYearRow(row: any): CatalogYear {
    return {
      id: Number(row.id),
      modelId: Number(row.model_id),
      year: Number(row.year),
      createdAt: this.toIsoDate(row.created_at),
      updatedAt: this.toIsoDate(row.updated_at),
    };
  }

  private mapTrimRow(row: any): CatalogTrim {
    return {
      id: Number(row.id),
      yearId: Number(row.model_year_id),
      name: row.name,
      createdAt: this.toIsoDate(row.created_at),
      updatedAt: this.toIsoDate(row.updated_at),
    };
  }

  private mapEngineRow(row: any): CatalogEngine {
    return {
      id: Number(row.id),
      trimId: Number(row.trim_id),
      name: row.name,
      displacement: row.displacement_l?.toString() ?? null,
      cylinders: row.cylinders !== null ? Number(row.cylinders) : null,
      fuel_type: row.fuel_type ?? null,
      createdAt: this.toIsoDate(row.created_at),
      updatedAt: this.toIsoDate(row.updated_at),
    };
  }

  private async assertMakeExists(client: PoolClient, makeId: number): Promise<void> {
    const result = await client.query(`SELECT 1 FROM ${VEHICLE_SCHEMA}.make WHERE id = $1`, [makeId]);
    if (result.rowCount === 0) {
      throw new Error(`Make ${makeId} not found`);
    }
  }

  private async assertModelExists(client: PoolClient, modelId: number): Promise<void> {
    const result = await client.query(`SELECT 1 FROM ${VEHICLE_SCHEMA}.model WHERE id = $1`, [modelId]);
    if (result.rowCount === 0) {
      throw new Error(`Model ${modelId} not found`);
    }
  }

  private async assertYearExists(client: PoolClient, yearId: number): Promise<void> {
    const result = await client.query(`SELECT 1 FROM ${VEHICLE_SCHEMA}.model_year WHERE id = $1`, [yearId]);
    if (result.rowCount === 0) {
      throw new Error(`Year ${yearId} not found`);
    }
  }

  private async assertTrimExists(client: PoolClient, trimId: number): Promise<void> {
    const result = await client.query(`SELECT 1 FROM ${VEHICLE_SCHEMA}.trim WHERE id = $1`, [trimId]);
    if (result.rowCount === 0) {
      throw new Error(`Trim ${trimId} not found`);
    }
  }

  private toIsoDate(value: Date | string | null | undefined): string {
    if (!value) {
      return new Date().toISOString();
    }
    if (value instanceof Date) {
      return value.toISOString();
    }
    const parsed = new Date(value);
    return Number.isNaN(parsed.valueOf()) ? new Date().toISOString() : parsed.toISOString();
  }

  private async logChange(
    client: PoolClient,
    changeType: 'CREATE' | 'UPDATE' | 'DELETE',
    resourceType: 'makes' | 'models' | 'years' | 'trims' | 'engines',
    resourceId: string,
    oldValue: any,
    newValue: any,
    changedBy: string
  ): Promise<void> {
    const query = `
      INSERT INTO platform_change_log (change_type, resource_type, resource_id, old_value, new_value, changed_by)
      VALUES ($1, $2, $3, $4, $5, $6)
    `;

    await client.query(query, [
      changeType,
      resourceType,
      resourceId,
      oldValue ? JSON.stringify(oldValue) : null,
      newValue ? JSON.stringify(newValue) : null,
      changedBy,
    ]);
  }
}
