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

interface IssueFilters {
  typ_id?: number[];
  prioritaet?: string[];
  status?: string[];
  erstellt_von?: string;
  zugewiesen_an?: string;
}

interface GetIssuesParams {
  userId: string;
  canViewAll: boolean;
  filters?: IssueFilters;
}

const BASE_SELECT = `
  SELECT i.*,
         it.name AS typ_name, it.icon AS typ_icon, it.farbe AS typ_farbe, it.erlaubt_abgelehnt AS typ_erlaubt_abgelehnt,
         u1.name AS erstellt_von_name,
         u2.name AS zugewiesen_an_name
  FROM issues i
  LEFT JOIN issue_typen it ON it.id = i.typ_id
  LEFT JOIN users u1 ON u1.id = i.erstellt_von
  LEFT JOIN users u2 ON u2.id = i.zugewiesen_an
`;

async function getIssues({ userId, canViewAll, filters }: GetIssuesParams) {
  try {
    const conditions: string[] = [];
    const values: any[] = [];
    let idx = 1;

    if (!canViewAll) {
      conditions.push(`(i.erstellt_von = $${idx} OR i.zugewiesen_an = $${idx})`);
      values.push(userId);
      idx++;
    }

    if (filters?.typ_id && filters.typ_id.length > 0) {
      conditions.push(`i.typ_id = ANY($${idx}::int[])`);
      values.push(filters.typ_id);
      idx++;
    }

    if (filters?.prioritaet && filters.prioritaet.length > 0) {
      conditions.push(`i.prioritaet = ANY($${idx}::text[])`);
      values.push(filters.prioritaet);
      idx++;
    }

    if (filters?.status && filters.status.length > 0) {
      conditions.push(`i.status = ANY($${idx}::text[])`);
      values.push(filters.status);
      idx++;
    }

    if (filters?.erstellt_von) {
      conditions.push(`i.erstellt_von = $${idx}`);
      values.push(filters.erstellt_von);
      idx++;
    }

    if (filters?.zugewiesen_an) {
      conditions.push(`i.zugewiesen_an = $${idx}`);
      values.push(filters.zugewiesen_an);
      idx++;
    }

    const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
    const query = `${BASE_SELECT} ${where} ORDER BY i.created_at DESC`;
    const result = await pool.query(query, values);
    return result.rows;
  } catch (error) {
    logger.error('IssueService.getIssues failed', { error });
    throw new Error('Issues konnten nicht geladen werden');
  }
}

async function getIssueById(id: number) {
  try {
    const result = await pool.query(
      `${BASE_SELECT} WHERE i.id = $1`,
      [id]
    );
    return result.rows[0] || null;
  } catch (error) {
    logger.error('IssueService.getIssueById failed', { error, id });
    throw new Error('Issue konnte nicht geladen werden');
  }
}

async function createIssue(
  data: { titel: string; beschreibung?: string; typ_id?: number; prioritaet?: string; faellig_am?: string | null },
  userId: string
) {
  try {
    const result = await pool.query(
      `INSERT INTO issues (titel, beschreibung, typ_id, prioritaet, erstellt_von, faellig_am)
       VALUES ($1, $2, $3, $4, $5, $6)
       RETURNING *`,
      [
        data.titel,
        data.beschreibung || null,
        data.typ_id || 3,
        data.prioritaet || 'mittel',
        userId,
        data.faellig_am || null,
      ]
    );
    return getIssueById(result.rows[0].id);
  } catch (error) {
    logger.error('IssueService.createIssue failed', { error });
    throw new Error('Issue konnte nicht erstellt werden');
  }
}

// Sentinel value to explicitly set zugewiesen_an to NULL (not used currently but kept for reference)
const UNASSIGN = '__UNASSIGN__';

async function updateIssue(
  id: number,
  data: {
    titel?: string;
    beschreibung?: string;
    typ_id?: number;
    prioritaet?: string;
    status?: string;
    zugewiesen_an?: string | null;
    faellig_am?: string | null;
  }
) {
  try {
    const setClauses: string[] = [];
    const values: any[] = [];
    let idx = 1;

    if (data.titel !== undefined) {
      setClauses.push(`titel = $${idx}`);
      values.push(data.titel);
      idx++;
    }
    if (data.beschreibung !== undefined) {
      setClauses.push(`beschreibung = $${idx}`);
      values.push(data.beschreibung);
      idx++;
    }
    if (data.typ_id !== undefined) {
      setClauses.push(`typ_id = $${idx}`);
      values.push(data.typ_id);
      idx++;
    }
    if (data.prioritaet !== undefined) {
      setClauses.push(`prioritaet = $${idx}`);
      values.push(data.prioritaet);
      idx++;
    }
    if (data.status !== undefined) {
      setClauses.push(`status = $${idx}`);
      values.push(data.status);
      idx++;
    }
    if ('zugewiesen_an' in data) {
      setClauses.push(`zugewiesen_an = $${idx}`);
      values.push(data.zugewiesen_an ?? null);
      idx++;
    }
    if ('faellig_am' in data) {
      setClauses.push(`faellig_am = $${idx}`);
      values.push(data.faellig_am ?? null);
      idx++;
    }

    if (setClauses.length === 0) {
      return getIssueById(id);
    }

    setClauses.push(`updated_at = NOW()`);
    values.push(id);

    const query = `
      UPDATE issues
      SET ${setClauses.join(', ')}
      WHERE id = $${idx}
      RETURNING id
    `;
    const result = await pool.query(query, values);
    if (result.rows.length === 0) return null;

    return getIssueById(id);
  } catch (error) {
    logger.error('IssueService.updateIssue failed', { error, id });
    throw new Error('Issue konnte nicht aktualisiert werden');
  }
}

async function addHistoryEntry(
  issueId: number,
  aktion: string,
  details: Record<string, unknown> | null,
  userId?: string,
) {
  try {
    await pool.query(
      `INSERT INTO issue_historie (issue_id, aktion, details, erstellt_von)
       VALUES ($1, $2, $3, $4)`,
      [issueId, aktion, details ? JSON.stringify(details) : null, userId || null],
    );
  } catch (error) {
    logger.error('IssueService.addHistoryEntry failed', { error, issueId });
  }
}

async function getHistory(issueId: number) {
  try {
    const result = await pool.query(
      `SELECT h.*, u.name AS erstellt_von_name
       FROM issue_historie h
       LEFT JOIN users u ON u.id = h.erstellt_von
       WHERE h.issue_id = $1
       ORDER BY h.erstellt_am DESC`,
      [issueId],
    );
    return result.rows;
  } catch (error) {
    logger.error('IssueService.getHistory failed', { error, issueId });
    return [];
  }
}

async function deleteIssue(id: number) {
  try {
    const result = await pool.query(
      `DELETE FROM issues WHERE id = $1 RETURNING id`,
      [id]
    );
    return result.rows.length > 0;
  } catch (error) {
    logger.error('IssueService.deleteIssue failed', { error, id });
    throw new Error('Issue konnte nicht gelöscht werden');
  }
}

async function getComments(issueId: number) {
  try {
    const result = await pool.query(
      `SELECT c.*, u.name AS autor_name
       FROM issue_kommentare c
       LEFT JOIN users u ON u.id = c.autor_id
       WHERE c.issue_id = $1
       ORDER BY c.created_at ASC`,
      [issueId]
    );
    return result.rows;
  } catch (error) {
    logger.error('IssueService.getComments failed', { error, issueId });
    throw new Error('Kommentare konnten nicht geladen werden');
  }
}

async function addComment(issueId: number, autorId: string, inhalt: string) {
  try {
    const result = await pool.query(
      `INSERT INTO issue_kommentare (issue_id, autor_id, inhalt)
       VALUES ($1, $2, $3)
       RETURNING *`,
      [issueId, autorId, inhalt]
    );
    return result.rows[0];
  } catch (error) {
    logger.error('IssueService.addComment failed', { error, issueId });
    throw new Error('Kommentar konnte nicht erstellt werden');
  }
}

async function getTypes() {
  try {
    const result = await pool.query(
      `SELECT * FROM issue_typen WHERE aktiv = true ORDER BY sort_order ASC, id ASC`
    );
    return result.rows;
  } catch (error) {
    logger.error('IssueService.getTypes failed', { error });
    throw new Error('Issue-Typen konnten nicht geladen werden');
  }
}

async function createType(data: {
  name: string;
  parent_id?: number | null;
  icon?: string | null;
  farbe?: string | null;
  erlaubt_abgelehnt?: boolean;
  sort_order?: number;
}) {
  try {
    const result = await pool.query(
      `INSERT INTO issue_typen (name, parent_id, icon, farbe, erlaubt_abgelehnt, sort_order)
       VALUES ($1, $2, $3, $4, $5, $6)
       RETURNING *`,
      [
        data.name,
        data.parent_id ?? null,
        data.icon ?? null,
        data.farbe ?? null,
        data.erlaubt_abgelehnt ?? true,
        data.sort_order ?? 0,
      ]
    );
    return result.rows[0];
  } catch (error) {
    logger.error('IssueService.createType failed', { error });
    throw new Error('Issue-Typ konnte nicht erstellt werden');
  }
}

async function updateType(
  id: number,
  data: {
    name?: string;
    parent_id?: number | null;
    icon?: string | null;
    farbe?: string | null;
    erlaubt_abgelehnt?: boolean;
    sort_order?: number;
  }
) {
  try {
    const setClauses: string[] = [];
    const values: any[] = [];
    let idx = 1;

    if (data.name !== undefined) {
      setClauses.push(`name = $${idx}`);
      values.push(data.name);
      idx++;
    }
    if ('parent_id' in data) {
      setClauses.push(`parent_id = $${idx}`);
      values.push(data.parent_id);
      idx++;
    }
    if ('icon' in data) {
      setClauses.push(`icon = $${idx}`);
      values.push(data.icon);
      idx++;
    }
    if ('farbe' in data) {
      setClauses.push(`farbe = $${idx}`);
      values.push(data.farbe);
      idx++;
    }
    if (data.erlaubt_abgelehnt !== undefined) {
      setClauses.push(`erlaubt_abgelehnt = $${idx}`);
      values.push(data.erlaubt_abgelehnt);
      idx++;
    }
    if (data.sort_order !== undefined) {
      setClauses.push(`sort_order = $${idx}`);
      values.push(data.sort_order);
      idx++;
    }

    if (setClauses.length === 0) {
      const result = await pool.query(`SELECT * FROM issue_typen WHERE id = $1`, [id]);
      return result.rows[0] || null;
    }

    values.push(id);
    const query = `UPDATE issue_typen SET ${setClauses.join(', ')} WHERE id = $${idx} RETURNING *`;
    const result = await pool.query(query, values);
    return result.rows[0] || null;
  } catch (error) {
    logger.error('IssueService.updateType failed', { error, id });
    throw new Error('Issue-Typ konnte nicht aktualisiert werden');
  }
}

async function deleteType(id: number) {
  try {
    const result = await pool.query(
      `DELETE FROM issue_typen WHERE id = $1 RETURNING *`,
      [id]
    );
    return result.rows[0] || null;
  } catch (error) {
    logger.error('IssueService.deleteType failed', { error, id });
    throw new Error('Issue-Typ konnte nicht gelöscht werden');
  }
}

async function getAssignableMembers() {
  try {
    const result = await pool.query(
      `SELECT id, name FROM users WHERE id IS NOT NULL ORDER BY name`
    );
    return result.rows;
  } catch (error) {
    logger.error('IssueService.getAssignableMembers failed', { error });
    throw new Error('Mitglieder konnten nicht geladen werden');
  }
}

async function getIssueCounts() {
  try {
    const result = await pool.query(`
      SELECT
        COALESCE(s.schluessel, i.status) AS schluessel,
        COALESCE(s.bezeichnung, i.status) AS bezeichnung,
        COALESCE(s.farbe, 'default') AS farbe,
        COALESCE(s.ist_abschluss, false) AS ist_abschluss,
        COALESCE(s.sort_order, 99) AS sort_order,
        COUNT(*)::int AS count
      FROM issues i
      LEFT JOIN issue_statuses s ON s.schluessel = i.status
      GROUP BY
        COALESCE(s.schluessel, i.status),
        COALESCE(s.bezeichnung, i.status),
        COALESCE(s.farbe, 'default'),
        COALESCE(s.ist_abschluss, false),
        COALESCE(s.sort_order, 99)
      ORDER BY COALESCE(s.sort_order, 99)
    `);
    return result.rows;
  } catch (error) {
    logger.error('IssueService.getIssueCounts failed', { error });
    throw new Error('Issue-Counts konnten nicht geladen werden');
  }
}

async function getIssueStatuses() {
  try {
    const result = await pool.query(
      `SELECT * FROM issue_statuses ORDER BY sort_order ASC, id ASC`
    );
    return result.rows;
  } catch (error) {
    logger.error('IssueService.getIssueStatuses failed', { error });
    throw new Error('Issue-Status konnten nicht geladen werden');
  }
}

async function createIssueStatus(data: {
  schluessel: string;
  bezeichnung: string;
  farbe?: string;
  ist_abschluss?: boolean;
  ist_initial?: boolean;
  benoetigt_typ_freigabe?: boolean;
  sort_order?: number;
}) {
  try {
    const result = await pool.query(
      `INSERT INTO issue_statuses (schluessel, bezeichnung, farbe, ist_abschluss, ist_initial, benoetigt_typ_freigabe, sort_order)
       VALUES ($1, $2, $3, $4, $5, $6, $7)
       RETURNING *`,
      [
        data.schluessel,
        data.bezeichnung,
        data.farbe ?? 'default',
        data.ist_abschluss ?? false,
        data.ist_initial ?? false,
        data.benoetigt_typ_freigabe ?? false,
        data.sort_order ?? 0,
      ]
    );
    return result.rows[0];
  } catch (error) {
    logger.error('IssueService.createIssueStatus failed', { error });
    throw new Error('Issue-Status konnte nicht erstellt werden');
  }
}

async function updateIssueStatus(id: number, data: {
  bezeichnung?: string;
  farbe?: string;
  ist_abschluss?: boolean;
  ist_initial?: boolean;
  benoetigt_typ_freigabe?: boolean;
  sort_order?: number;
  aktiv?: boolean;
}) {
  try {
    const setClauses: string[] = [];
    const values: any[] = [];
    let idx = 1;

    if (data.bezeichnung !== undefined) { setClauses.push(`bezeichnung = $${idx}`); values.push(data.bezeichnung); idx++; }
    if (data.farbe !== undefined) { setClauses.push(`farbe = $${idx}`); values.push(data.farbe); idx++; }
    if (data.ist_abschluss !== undefined) { setClauses.push(`ist_abschluss = $${idx}`); values.push(data.ist_abschluss); idx++; }
    if (data.ist_initial !== undefined) { setClauses.push(`ist_initial = $${idx}`); values.push(data.ist_initial); idx++; }
    if (data.benoetigt_typ_freigabe !== undefined) { setClauses.push(`benoetigt_typ_freigabe = $${idx}`); values.push(data.benoetigt_typ_freigabe); idx++; }
    if (data.sort_order !== undefined) { setClauses.push(`sort_order = $${idx}`); values.push(data.sort_order); idx++; }
    if (data.aktiv !== undefined) { setClauses.push(`aktiv = $${idx}`); values.push(data.aktiv); idx++; }

    if (setClauses.length === 0) {
      const r = await pool.query(`SELECT * FROM issue_statuses WHERE id = $1`, [id]);
      return r.rows[0] || null;
    }
    values.push(id);
    const result = await pool.query(
      `UPDATE issue_statuses SET ${setClauses.join(', ')} WHERE id = $${idx} RETURNING *`,
      values
    );
    return result.rows[0] || null;
  } catch (error) {
    logger.error('IssueService.updateIssueStatus failed', { error, id });
    throw new Error('Issue-Status konnte nicht aktualisiert werden');
  }
}

async function deleteIssueStatus(id: number) {
  try {
    const result = await pool.query(
      `DELETE FROM issue_statuses WHERE id = $1 RETURNING *`,
      [id]
    );
    return result.rows[0] || null;
  } catch (error) {
    logger.error('IssueService.deleteIssueStatus failed', { error, id });
    throw new Error('Issue-Status konnte nicht gelöscht werden');
  }
}

async function getIssuePriorities() {
  try {
    const result = await pool.query(
      `SELECT * FROM issue_prioritaeten ORDER BY sort_order ASC, id ASC`
    );
    return result.rows;
  } catch (error) {
    logger.error('IssueService.getIssuePriorities failed', { error });
    throw new Error('Issue-Prioritäten konnten nicht geladen werden');
  }
}

async function createIssuePriority(data: {
  schluessel: string;
  bezeichnung: string;
  farbe?: string;
  sort_order?: number;
}) {
  try {
    const result = await pool.query(
      `INSERT INTO issue_prioritaeten (schluessel, bezeichnung, farbe, sort_order)
       VALUES ($1, $2, $3, $4)
       RETURNING *`,
      [data.schluessel, data.bezeichnung, data.farbe ?? '#9e9e9e', data.sort_order ?? 0]
    );
    return result.rows[0];
  } catch (error) {
    logger.error('IssueService.createIssuePriority failed', { error });
    throw new Error('Priorität konnte nicht erstellt werden');
  }
}

async function updateIssuePriority(id: number, data: {
  bezeichnung?: string;
  farbe?: string;
  sort_order?: number;
  aktiv?: boolean;
}) {
  try {
    const setClauses: string[] = [];
    const values: any[] = [];
    let idx = 1;

    if (data.bezeichnung !== undefined) { setClauses.push(`bezeichnung = $${idx}`); values.push(data.bezeichnung); idx++; }
    if (data.farbe !== undefined) { setClauses.push(`farbe = $${idx}`); values.push(data.farbe); idx++; }
    if (data.sort_order !== undefined) { setClauses.push(`sort_order = $${idx}`); values.push(data.sort_order); idx++; }
    if (data.aktiv !== undefined) { setClauses.push(`aktiv = $${idx}`); values.push(data.aktiv); idx++; }

    if (setClauses.length === 0) {
      const r = await pool.query(`SELECT * FROM issue_prioritaeten WHERE id = $1`, [id]);
      return r.rows[0] || null;
    }
    values.push(id);
    const result = await pool.query(
      `UPDATE issue_prioritaeten SET ${setClauses.join(', ')} WHERE id = $${idx} RETURNING *`,
      values
    );
    return result.rows[0] || null;
  } catch (error) {
    logger.error('IssueService.updateIssuePriority failed', { error, id });
    throw new Error('Priorität konnte nicht aktualisiert werden');
  }
}

async function deleteIssuePriority(id: number) {
  try {
    const result = await pool.query(
      `DELETE FROM issue_prioritaeten WHERE id = $1 RETURNING *`,
      [id]
    );
    return result.rows[0] || null;
  } catch (error) {
    logger.error('IssueService.deleteIssuePriority failed', { error, id });
    throw new Error('Priorität konnte nicht gelöscht werden');
  }
}


// ---------------------------------------------------------------------------
// Files (Issue Dateien)
// ---------------------------------------------------------------------------

async function addFile(
  issueId: number,
  fileData: { dateiname: string; dateipfad: string; dateityp: string; dateigroesse: number },
  userId: string
) {
  try {
    const result = await pool.query(
      `INSERT INTO issue_dateien (issue_id, dateiname, dateipfad, dateityp, dateigroesse, hochgeladen_von)
       VALUES ($1, $2, $3, $4, $5, $6)
       RETURNING *`,
      [issueId, fileData.dateiname, fileData.dateipfad, fileData.dateityp, fileData.dateigroesse, userId]
    );
    return result.rows[0];
  } catch (error) {
    logger.error('IssueService.addFile failed', { error, issueId });
    throw new Error('Datei konnte nicht gespeichert werden');
  }
}

async function getFiles(issueId: number) {
  try {
    const result = await pool.query(
      `SELECT * FROM issue_dateien WHERE issue_id = $1 ORDER BY hochgeladen_am DESC`,
      [issueId]
    );
    return result.rows;
  } catch (error) {
    logger.error('IssueService.getFiles failed', { error, issueId });
    throw new Error('Dateien konnten nicht geladen werden');
  }
}

async function deleteFile(fileId: string, _userId: string) {
  try {
    const fileResult = await pool.query(
      `SELECT * FROM issue_dateien WHERE id = $1`,
      [fileId]
    );
    if (fileResult.rows.length === 0) return null;

    const file = fileResult.rows[0];
    await pool.query(`DELETE FROM issue_dateien WHERE id = $1`, [fileId]);

    // Remove from disk
    try {
      if (file.dateipfad && fs.existsSync(file.dateipfad)) {
        fs.unlinkSync(file.dateipfad);
      }
    } catch (err) {
      logger.warn('Failed to delete issue file from disk', { path: file.dateipfad, error: err });
    }

    return { dateipfad: file.dateipfad, dateiname: file.dateiname };
  } catch (error) {
    logger.error('IssueService.deleteFile failed', { error, fileId });
    throw new Error('Datei konnte nicht gelöscht werden');
  }
}

// ---------------------------------------------------------------------------
// Overdue Issues
// ---------------------------------------------------------------------------

async function getOverdueIssues() {
  try {
    const result = await pool.query(`
      SELECT i.*, u1.name AS erstellt_von_name, u2.name AS zugewiesen_an_name
      FROM issues i
      LEFT JOIN users u1 ON u1.id = i.erstellt_von
      LEFT JOIN users u2 ON u2.id = i.zugewiesen_an
      LEFT JOIN issue_statuses s ON s.schluessel = i.status
      WHERE i.faellig_am < NOW()
        AND (s.ist_abschluss = false OR s.ist_abschluss IS NULL)
      ORDER BY i.faellig_am ASC
    `);
    return result.rows;
  } catch (error) {
    logger.error('IssueService.getOverdueIssues failed', { error });
    throw new Error('Überfällige Issues konnten nicht geladen werden');
  }
}

export default {
  getIssues,
  getIssueById,
  createIssue,
  updateIssue,
  deleteIssue,
  getComments,
  addComment,
  getTypes,
  createType,
  updateType,
  deleteType,
  getAssignableMembers,
  getIssueCounts,
  getIssueStatuses,
  addHistoryEntry,
  getHistory,
  createIssueStatus,
  updateIssueStatus,
  deleteIssueStatus,
  getIssuePriorities,
  createIssuePriority,
  updateIssuePriority,
  deleteIssuePriority,
  addFile,
  getFiles,
  deleteFile,
  getOverdueIssues,
  UNASSIGN,
};
