const client = require("../graphql-client/graphql-client").client;
const {
  UPDATE_MESSAGE_STATUS,
  MARK_MESSAGES_AS_READ,
  INSERT_PHONE_NUMBER_OPT_OUT,
  FIND_BODYSHOP_BY_MESSAGING_SERVICE_SID
} = require("../graphql-client/queries");
const logger = require("../utils/logger");
const { phone } = require("phone");

// Local GraphQL for “mark unread” (kept here to avoid requiring edits to graphql-client/queries.js)
const GET_LAST_INBOUND_NON_SYSTEM_MESSAGE = `
  query GetLastInboundNonSystemMessage($conversationId: uuid!) {
    messages(
      where: {
        conversationid: { _eq: $conversationId }
        isoutbound: { _eq: false }
        is_system: { _eq: false }
      }
      order_by: { created_at: desc }
      limit: 1
    ) {
      id
      created_at
      read
    }
  }
`;

const MARK_LAST_INBOUND_MESSAGE_UNREAD_MAX_ONE = `
  mutation MarkLastInboundMessageUnreadMaxOne($conversationId: uuid!, $lastId: uuid!) {
    markOthersRead: update_messages(
      _set: { read: true }
      where: {
        conversationid: { _eq: $conversationId }
        isoutbound: { _eq: false }
        is_system: { _eq: false }
        id: { _neq: $lastId }
      }
    ) {
      affected_rows
      returning { id }
    }

    markLastUnread: update_messages_by_pk(pk_columns: { id: $lastId }, _set: { read: false }) {
      id
    }
  }
`;

/**
 * Handle the status of an SMS message
 * @param req
 * @param res
 * @returns {Promise<*>}
 */
const status = async (req, res) => {
  const { SmsSid, SmsStatus, ErrorCode, To, MessagingServiceSid } = req.body;
  const {
    ioRedis,
    ioHelpers: { getBodyshopRoom, getBodyshopConversationRoom }
  } = req;

  try {
    // Ignore status 'queued'
    if (SmsStatus === "queued") {
      return res.status(200).json({ message: "Status 'queued' disregarded." });
    }

    // Handle ErrorCode 21610 (Attempt to send to unsubscribed recipient) first
    if (ErrorCode === "21610" && To && MessagingServiceSid) {
      try {
        // Step 1: Find the bodyshop by MessagingServiceSid
        const bodyshopResponse = await client.request(FIND_BODYSHOP_BY_MESSAGING_SERVICE_SID, {
          mssid: MessagingServiceSid,
          phone: phone(To).phoneNumber // Pass the normalized phone number as required
        });

        const bodyshop = bodyshopResponse.bodyshops[0];
        if (!bodyshop) {
          logger.log("sms-opt-out-error", "ERROR", "api", null, {
            msid: SmsSid,
            messagingServiceSid: MessagingServiceSid,
            to: To,
            error: "No matching bodyshop found"
          });
        } else {
          // Step 2: Insert into phone_number_opt_out table
          const now = new Date().toISOString();
          const optOutInput = {
            bodyshopid: bodyshop.id,
            phone_number: phone(To).phoneNumber.replace(/^\+1/, ""), // Normalize phone number (remove +1 for CA numbers)
            created_at: now,
            updated_at: now
          };

          const optOutResponse = await client.request(INSERT_PHONE_NUMBER_OPT_OUT, {
            optOutInput: [optOutInput]
          });

          logger.log("sms-opt-out-success", "INFO", null, null, {
            msid: SmsSid,
            bodyshopid: bodyshop.id,
            phone_number: optOutInput.phone_number,
            affected_rows: optOutResponse.insert_phone_number_opt_out.affected_rows
          });

          // Store bodyshopid for potential use in WebSocket notification
          const broadcastRoom = getBodyshopRoom(bodyshop.id);
          ioRedis.to(broadcastRoom).emit("phone-number-opted-out", {
            bodyshopid: bodyshop.id,
            phone_number: optOutInput.phone_number
            // Note: conversationId is not included yet; will be set after message lookup
          });
        }
      } catch (error) {
        logger.log("sms-opt-out-error", "ERROR", "api", null, {
          msid: SmsSid,
          messagingServiceSid: MessagingServiceSid,
          to: To,
          error: error.message,
          stack: error.stack
        });
        // Continue processing to update message status
      }
    }

    // Update message status in the database
    const response = await client.request(UPDATE_MESSAGE_STATUS, {
      msid: SmsSid,
      fields: { status: SmsStatus }
    });

    const message = response.update_messages?.returning?.[0];

    if (message) {
      logger.log("sms-status-update", "DEBUG", "api", null, {
        msid: SmsSid,
        status: SmsStatus
      });

      // Emit WebSocket event to notify the change in message status
      const conversationRoom = getBodyshopConversationRoom({
        bodyshopId: message.conversation.bodyshopid,
        conversationId: message.conversationid
      });

      ioRedis.to(conversationRoom).emit("message-changed", {
        ...message,
        status: SmsStatus,
        type: "status-changed"
      });
    } else {
      logger.log("sms-status-update-warning", "WARN", null, null, {
        msid: SmsSid,
        status: SmsStatus,
        warning: "No message found in database for update"
      });
    }

    res.sendStatus(200);
  } catch (err) {
    logger.log("sms-status-update-error", "ERROR", "api", null, {
      msid: SmsSid,
      status: SmsStatus,
      error: err.message,
      stack: err.stack
    });
    res.status(500).json({ error: "Failed to update message status." });
  }
};

/**
 * Mark a conversation as read
 * @param req
 * @param res
 * @returns {Promise<*>}
 */
const markConversationRead = async (req, res) => {
  const {
    ioRedis,
    ioHelpers: { getBodyshopRoom }
  } = req;
  const { conversation, imexshopid, bodyshopid } = req.body;

  // Alternatively, support both payload formats
  const conversationId = conversation?.id || req.body.conversationId;

  if (!conversationId || !imexshopid || !bodyshopid) {
    return res.status(400).json({ error: "Invalid conversation data provided." });
  }

  try {
    const response = await client.request(MARK_MESSAGES_AS_READ, {
      conversationId
    });

    const updatedMessageIds = response.update_messages.returning.map((message) => message.id);

    const broadcastRoom = getBodyshopRoom(bodyshopid);

    ioRedis.to(broadcastRoom).emit("conversation-changed", {
      type: "conversation-marked-read",
      conversationId,
      affectedMessages: response.update_messages.affected_rows,
      messageIds: updatedMessageIds
    });

    res.status(200).json({
      success: true,
      message: "Conversation marked as read."
    });
  } catch (error) {
    console.error("Error marking conversation as read:", error);
    res.status(500).json({ error: "Failed to mark conversation as read." });
  }
};

/**
 * Mark last inbound (customer) non-system message as unread.
 * Enforces: max unread inbound non-system messages per thread = 1
 *
 * Body: { conversationId, imexshopid, bodyshopid }
 */
const markLastMessageUnread = async (req, res) => {
  const {
    ioRedis,
    ioHelpers: { getBodyshopRoom }
  } = req;

  const { conversationId, imexshopid, bodyshopid } = req.body;

  if (!conversationId || !imexshopid || !bodyshopid) {
    return res.status(400).json({ error: "Invalid conversation data provided." });
  }

  try {
    const lastResp = await client.request(GET_LAST_INBOUND_NON_SYSTEM_MESSAGE, { conversationId });
    const last = lastResp?.messages?.[0];

    if (!last?.id) {
      // No inbound message to mark unread
      const broadcastRoom = getBodyshopRoom(bodyshopid);
      ioRedis.to(broadcastRoom).emit("conversation-changed", {
        type: "conversation-marked-unread",
        conversationId,
        lastUnreadMessageId: null,
        messageIdsMarkedRead: [],
        unreadCount: 0
      });

      return res.status(200).json({
        success: true,
        conversationId,
        lastUnreadMessageId: null,
        messageIdsMarkedRead: [],
        unreadCount: 0
      });
    }

    const mutResp = await client.request(MARK_LAST_INBOUND_MESSAGE_UNREAD_MAX_ONE, {
      conversationId,
      lastId: last.id
    });

    const messageIdsMarkedRead = mutResp?.markOthersRead?.returning?.map((m) => m.id) || [];

    const broadcastRoom = getBodyshopRoom(bodyshopid);

    ioRedis.to(broadcastRoom).emit("conversation-changed", {
      type: "conversation-marked-unread",
      conversationId,
      lastUnreadMessageId: last.id,
      messageIdsMarkedRead,
      unreadCount: 1
    });

    return res.status(200).json({
      success: true,
      conversationId,
      lastUnreadMessageId: last.id,
      messageIdsMarkedRead,
      unreadCount: 1
    });
  } catch (error) {
    console.error("Error marking last message unread:", error);
    return res.status(500).json({ error: "Failed to mark last message unread." });
  }
};

module.exports = {
  status,
  markConversationRead,
  markLastMessageUnread
};
