import pool from '../config/database';
import logger from '../utils/logger';

interface PersonalEquipmentFilters {
  userId?: string;
  kategorie?: string;
  zustand?: string;
}

interface CreatePersonalEquipmentData {
  bezeichnung: string;
  kategorie?: string;
  artikel_id?: number;
  user_id?: string;
  benutzer_name?: string;
  groesse?: string;
  seriennummer?: string;
  inventarnummer?: string;
  anschaffung_datum?: string;
  zustand?: string;
  notizen?: string;
  eigenschaften?: { eigenschaft_id?: number | null; name: string; wert: string }[];
}

interface UpdatePersonalEquipmentData {
  bezeichnung?: string;
  kategorie?: string;
  artikel_id?: number | null;
  user_id?: string | null;
  benutzer_name?: string | null;
  groesse?: string | null;
  seriennummer?: string | null;
  inventarnummer?: string | null;
  anschaffung_datum?: string | null;
  zustand?: string;
  notizen?: string | null;
  eigenschaften?: { eigenschaft_id?: number | null; name: string; wert: string }[] | null;
}

const BASE_SELECT = `
  SELECT pa.*,
         COALESCE(u.given_name || ' ' || u.family_name, u.name) AS user_display_name,
         aa.bezeichnung AS artikel_bezeichnung,
         akk.name AS artikel_kategorie_name,
         akk_parent.name AS artikel_kategorie_parent_name
  FROM persoenliche_ausruestung pa
  LEFT JOIN users u ON u.id = pa.user_id
  LEFT JOIN ausruestung_artikel aa ON aa.id = pa.artikel_id
  LEFT JOIN ausruestung_kategorien_katalog akk ON akk.id = aa.kategorie_id
  LEFT JOIN ausruestung_kategorien_katalog akk_parent ON akk_parent.id = akk.parent_id
  WHERE pa.geloescht_am IS NULL
`;

async function loadEigenschaften(ids: string[]) {
  if (ids.length === 0) return new Map<string, { id: number; persoenlich_id: string; eigenschaft_id: number | null; name: string; wert: string }[]>();
  const result = await pool.query(
    `SELECT pae.id, pae.persoenlich_id, pae.eigenschaft_id, pae.name, pae.wert
     FROM persoenliche_ausruestung_eigenschaften pae
     WHERE pae.persoenlich_id = ANY($1)`,
    [ids],
  );
  const map = new Map<string, typeof result.rows>();
  for (const row of result.rows) {
    const arr = map.get(row.persoenlich_id) || [];
    arr.push(row);
    map.set(row.persoenlich_id, arr);
  }
  return map;
}

async function getAll(filters: PersonalEquipmentFilters = {}) {
  try {
    const conditions: string[] = [];
    const params: unknown[] = [];

    if (filters.userId) {
      params.push(filters.userId);
      conditions.push(`pa.user_id = $${params.length}`);
    }
    if (filters.kategorie) {
      params.push(filters.kategorie);
      conditions.push(`pa.kategorie = $${params.length}`);
    }
    if (filters.zustand) {
      params.push(filters.zustand);
      conditions.push(`pa.zustand = $${params.length}`);
    }

    const where = conditions.length > 0 ? ` AND ${conditions.join(' AND ')}` : '';
    const result = await pool.query(
      `${BASE_SELECT}${where} ORDER BY pa.bezeichnung`,
      params,
    );

    const ids = result.rows.map((r: { id: string }) => r.id);
    const eigMap = await loadEigenschaften(ids);
    for (const row of result.rows) {
      row.eigenschaften = eigMap.get(row.id) || [];
    }

    return result.rows;
  } catch (error) {
    logger.error('personalEquipmentService.getAll failed', { error });
    throw new Error('Failed to fetch personal equipment');
  }
}

async function getByUserId(userId: string) {
  try {
    const result = await pool.query(
      `${BASE_SELECT} AND pa.user_id = $1 ORDER BY pa.bezeichnung`,
      [userId],
    );

    const ids = result.rows.map((r: { id: string }) => r.id);
    const eigMap = await loadEigenschaften(ids);
    for (const row of result.rows) {
      row.eigenschaften = eigMap.get(row.id) || [];
    }

    return result.rows;
  } catch (error) {
    logger.error('personalEquipmentService.getByUserId failed', { error, userId });
    throw new Error('Failed to fetch personal equipment for user');
  }
}

async function getById(id: string) {
  try {
    const result = await pool.query(
      `${BASE_SELECT} AND pa.id = $1`,
      [id],
    );
    if (!result.rows[0]) return null;

    const eigResult = await pool.query(
      `SELECT pae.id, pae.persoenlich_id, pae.eigenschaft_id, pae.name, pae.wert
       FROM persoenliche_ausruestung_eigenschaften pae
       WHERE pae.persoenlich_id = $1`,
      [id],
    );
    result.rows[0].eigenschaften = eigResult.rows;

    return result.rows[0];
  } catch (error) {
    logger.error('personalEquipmentService.getById failed', { error, id });
    throw new Error('Failed to fetch personal equipment item');
  }
}

async function create(data: CreatePersonalEquipmentData, requestingUserId: string) {
  try {
    const result = await pool.query(
      `INSERT INTO persoenliche_ausruestung (
         bezeichnung, kategorie, artikel_id, user_id, benutzer_name,
         groesse, seriennummer, inventarnummer, anschaffung_datum,
         zustand, notizen, erstellt_von
       ) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12)
       RETURNING *`,
      [
        data.bezeichnung,
        data.kategorie ?? null,
        data.artikel_id ?? null,
        data.user_id ?? null,
        data.benutzer_name ?? null,
        data.groesse ?? null,
        data.seriennummer ?? null,
        data.inventarnummer ?? null,
        data.anschaffung_datum ?? null,
        data.zustand ?? 'gut',
        data.notizen ?? null,
        requestingUserId,
      ],
    );
    const created = result.rows[0];
    logger.info('Personal equipment created', { id: created.id, by: requestingUserId });

    if (data.eigenschaften && data.eigenschaften.length > 0) {
      for (const e of data.eigenschaften) {
        await pool.query(
          `INSERT INTO persoenliche_ausruestung_eigenschaften (persoenlich_id, eigenschaft_id, name, wert)
           VALUES ($1, $2, $3, $4)`,
          [created.id, e.eigenschaft_id ?? null, e.name, e.wert],
        );
      }
    }

    return created;
  } catch (error) {
    logger.error('personalEquipmentService.create failed', { error });
    throw new Error('Failed to create personal equipment');
  }
}

async function update(id: string, data: UpdatePersonalEquipmentData) {
  try {
    const fields: string[] = [];
    const values: unknown[] = [];
    let p = 1;

    const addField = (col: string, value: unknown) => {
      fields.push(`${col} = $${p++}`);
      values.push(value);
    };

    if (data.bezeichnung !== undefined)       addField('bezeichnung', data.bezeichnung);
    if (data.kategorie !== undefined)         addField('kategorie', data.kategorie);
    if (data.artikel_id !== undefined)        addField('artikel_id', data.artikel_id);
    if (data.user_id !== undefined)           addField('user_id', data.user_id);
    if (data.benutzer_name !== undefined)     addField('benutzer_name', data.benutzer_name);
    if (data.groesse !== undefined)           addField('groesse', data.groesse);
    if (data.seriennummer !== undefined)      addField('seriennummer', data.seriennummer);
    if (data.inventarnummer !== undefined)    addField('inventarnummer', data.inventarnummer);
    if (data.anschaffung_datum !== undefined) addField('anschaffung_datum', data.anschaffung_datum);
    if (data.zustand !== undefined)           addField('zustand', data.zustand);
    if (data.notizen !== undefined)           addField('notizen', data.notizen);

    if (fields.length === 0 && data.eigenschaften === undefined) {
      throw new Error('No fields to update');
    }

    let updated = null;
    if (fields.length > 0) {
      values.push(id);
      const result = await pool.query(
        `UPDATE persoenliche_ausruestung SET ${fields.join(', ')} WHERE id = $${p} AND geloescht_am IS NULL RETURNING *`,
        values,
      );
      if (result.rows.length === 0) return null;
      updated = result.rows[0];
    }

    if (data.eigenschaften !== undefined) {
      await pool.query('DELETE FROM persoenliche_ausruestung_eigenschaften WHERE persoenlich_id = $1', [id]);
      if (data.eigenschaften && data.eigenschaften.length > 0) {
        for (const e of data.eigenschaften) {
          await pool.query(
            `INSERT INTO persoenliche_ausruestung_eigenschaften (persoenlich_id, eigenschaft_id, name, wert)
             VALUES ($1, $2, $3, $4)`,
            [id, e.eigenschaft_id ?? null, e.name, e.wert],
          );
        }
      }
    }

    if (!updated) {
      // Only eigenschaften were updated, fetch the row
      const result = await pool.query(
        `SELECT * FROM persoenliche_ausruestung WHERE id = $1 AND geloescht_am IS NULL`,
        [id],
      );
      if (result.rows.length === 0) return null;
      updated = result.rows[0];
    }

    logger.info('Personal equipment updated', { id });
    return updated;
  } catch (error) {
    logger.error('personalEquipmentService.update failed', { error, id });
    throw error;
  }
}

async function softDelete(id: string) {
  try {
    const result = await pool.query(
      `UPDATE persoenliche_ausruestung SET geloescht_am = NOW() WHERE id = $1 AND geloescht_am IS NULL RETURNING id`,
      [id],
    );
    if (result.rows.length === 0) return false;
    logger.info('Personal equipment soft-deleted', { id });
    return true;
  } catch (error) {
    logger.error('personalEquipmentService.softDelete failed', { error, id });
    throw new Error('Failed to delete personal equipment');
  }
}

export default {
  getAll,
  getByUserId,
  getById,
  create,
  update,
  delete: softDelete,
};
