import { pool } from "../config/db.js";
import { TICKET_STATUS } from "../constants/ticketStatus.js";
import { ApiError } from "../utils/ApiError.js";
import { getJwtUserId } from "../middlewares/authMiddleware.js";
import {
  createTicket,
  deleteTicket,
  getTicketById,
  listTicketLogs,
  listTickets,
  listTicketStatuses,
  listTicketTypes,
  updateTicket,
} from "../repositories/ticketsRepository.js";
import { asyncHandler } from "../middlewares/asyncHandler.js";

export const getAllTickets = asyncHandler(async (req, res) => {
  const rows = await listTickets();
  res.json({ success: true, data: rows });
});

export const getTicket = asyncHandler(async (req, res) => {
  const id = Number(req.params.id);
  if (!Number.isInteger(id) || id < 1) {
    throw new ApiError(400, "Invalid ticket id");
  }
  const ticket = await getTicketById(id);
  if (!ticket) {
    throw new ApiError(404, "Ticket not found");
  }
  res.json({ success: true, data: ticket });
});

export const getTicketTypes = asyncHandler(async (req, res) => {
  const rows = await listTicketTypes();
  res.json({ success: true, data: rows });
});

export const getTicketStatuses = asyncHandler(async (req, res) => {
  const rows = await listTicketStatuses();
  res.json({ success: true, data: rows });
});

export const getTicketsLogs = asyncHandler(async (req, res) => {
  const rows = await listTicketLogs({
    limit: req.query.limit,
    ticketId: req.query.ticket_id,
    statusId: req.query.status_id,
  });
  res.json({ success: true, data: rows });
});

export const postTicket = asyncHandler(async (req, res) => {
  const user_id = req.body.user_id ?? getJwtUserId(req.user);
  const ticket_type_id = Number(req.body.ticket_type_id);
  const description = req.body.description?.trim?.() ?? req.body.description;
  const subject = req.body.subject?.trim?.() ?? req.body.subject;

  if (!user_id || !ticket_type_id) {
    throw new ApiError(400, "user_id and ticket_type_id are required");
  }
  if (!description && !subject) {
    throw new ApiError(400, "description is required");
  }

  const fullDescription = subject
    ? `${subject}${description ? `\n\n${description}` : ""}`
    : description;

  const connection = await pool.getConnection();
  try {
    await connection.beginTransaction();
    const ticketId = await createTicket(
      {
        user_id: Number(user_id),
        ticket_type_id,
        status_id: TICKET_STATUS.OPEN,
        description: fullDescription,
      },
      connection
    );
    await connection.commit();
    const ticket = await getTicketById(ticketId);
    res.status(201).json({ success: true, data: ticket });
  } catch (err) {
    await connection.rollback();
    throw err;
  } finally {
    connection.release();
  }
});

export const putTicket = asyncHandler(async (req, res) => {
  const id = Number(req.params.id);
  if (!Number.isInteger(id) || id < 1) {
    throw new ApiError(400, "Invalid ticket id");
  }

  const updateData = {};
  if (req.body.user_id !== undefined) updateData.user_id = req.body.user_id;
  if (req.body.ticket_type_id !== undefined) {
    updateData.ticket_type_id = req.body.ticket_type_id;
  }
  if (req.body.status_id !== undefined) updateData.status_id = req.body.status_id;
  if (req.body.description !== undefined) {
    updateData.description = req.body.description;
  }
  if (req.body.closed_at !== undefined) updateData.closed_at = req.body.closed_at;

  const connection = await pool.getConnection();
  try {
    await connection.beginTransaction();
    const updated = await updateTicket(id, updateData, connection);
    if (!updated) {
      throw new ApiError(404, "Ticket not found");
    }
    await connection.commit();
    const ticket = await getTicketById(id);
    res.json({ success: true, data: ticket });
  } catch (err) {
    await connection.rollback();
    throw err;
  } finally {
    connection.release();
  }
});

export const closeTicket = asyncHandler(async (req, res) => {
  const id = Number(req.params.id);
  if (!Number.isInteger(id) || id < 1) {
    throw new ApiError(400, "Invalid ticket id");
  }

  const connection = await pool.getConnection();
  try {
    await connection.beginTransaction();
    const updated = await updateTicket(
      id,
      { status_id: TICKET_STATUS.RESOLVED, closed_at: new Date() },
      connection
    );
    if (!updated) {
      throw new ApiError(404, "Ticket not found");
    }
    await connection.commit();
    const ticket = await getTicketById(id);
    res.json({ success: true, data: ticket });
  } catch (err) {
    await connection.rollback();
    throw err;
  } finally {
    connection.release();
  }
});

export const removeTicket = asyncHandler(async (req, res) => {
  const id = Number(req.params.id);
  if (!Number.isInteger(id) || id < 1) {
    throw new ApiError(400, "Invalid ticket id");
  }

  const connection = await pool.getConnection();
  try {
    await connection.beginTransaction();
    const deleted = await deleteTicket(id, connection);
    if (!deleted) {
      throw new ApiError(404, "Ticket not found");
    }
    await connection.commit();
    res.json({ success: true, message: "Ticket deleted" });
  } catch (err) {
    await connection.rollback();
    throw err;
  } finally {
    connection.release();
  }
});

export const startTicket = asyncHandler(async (req, res) => {
  const id = Number(req.params.id);
  if (!Number.isInteger(id) || id < 1) {
    throw new ApiError(400, "Invalid ticket id");
  }

  const connection = await pool.getConnection();
  try {
    await connection.beginTransaction();
    const updated = await updateTicket(
      id,
      { status_id: TICKET_STATUS.IN_PROGRESS },
      connection
    );
    if (!updated) {
      throw new ApiError(404, "Ticket not found");
    }
    await connection.commit();
    const ticket = await getTicketById(id);
    res.json({ success: true, data: ticket });
  } catch (err) {
    await connection.rollback();
    throw err;
  } finally {
    connection.release();
  }
});
