import { Client, TextChannel, ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js";
import fs from "fs";
import path from "path";
import { BorrowRequest } from "@src/types";
import { cfg } from "@systems/config";
import { getCharacterByName } from "@systems/characters";

const PREFS_PATH = path.join(__dirname, "../../data/sessionPreferences.json");

// ─── Persistent preferences ───────────────────────────────────────────────────
let _prefs: Record<string, { ownerKey: string; charName: string }> = {};

function loadPrefs(): void {
  try { _prefs = JSON.parse(fs.readFileSync(PREFS_PATH, "utf8")); }
  catch { _prefs = {}; }
}

function savePrefs(): void {
  try { fs.writeFileSync(PREFS_PATH, JSON.stringify(_prefs, null, 2)); }
  catch (err) { console.error("Failed to save sessionPreferences.json:", err); }
}

loadPrefs();

export function setPersistentPreference(userKey: string, ownerKey: string, charName: string): void {
  _prefs[userKey] = { ownerKey, charName };
  savePrefs();
}

export function clearPersistentPreference(userKey: string): void {
  delete _prefs[userKey];
  savePrefs();
}

export function getPersistentPreference(userKey: string): { ownerKey: string; charName: string } | null {
  return _prefs[userKey] ?? null;
}

// ─── Active borrow requests ───────────────────────────────────────────────────
const pendingRequests: Map<string, BorrowRequest> = new Map();
const sessionBorrows: Map<string, { ownerKey: string; charName: string }> = new Map();
const borrowDmMessages: Map<string, { channelId: string; messageId: string }> = new Map();

function requestKey(ownerKey: string, requesterKey: string): string {
  return `${ownerKey}:${requesterKey}`;
}

export function getPendingRequest(ownerKey: string, requesterKey: string): BorrowRequest | null {
  return pendingRequests.get(requestKey(ownerKey, requesterKey)) ?? null;
}

export function getPendingRequestByKey(key: string): BorrowRequest | null {
  return pendingRequests.get(key) ?? null;
}

export function getAllPendingForOwner(ownerKey: string): BorrowRequest[] {
  return [...pendingRequests.values()].filter((r) => r.ownerKey === ownerKey);
}

export function addPendingRequest(request: BorrowRequest): void {
  const key    = requestKey(request.ownerKey, request.requesterKey);
  const expiry = cfg("borrowRequestExpiryMs" as any) ?? 0;
  pendingRequests.set(key, request);
  if (expiry > 0) {
    setTimeout(() => {
      if (pendingRequests.get(key)?.requestedAt === request.requestedAt) {
        pendingRequests.delete(key);
        console.log(`[borrow] Request ${key} expired.`);
      }
    }, expiry);
  }
}

export function removePendingRequest(ownerKey: string, requesterKey: string): void {
  pendingRequests.delete(requestKey(ownerKey, requesterKey));
}

export function storeDmMessage(ownerKey: string, requesterKey: string, channelId: string, messageId: string): void {
  borrowDmMessages.set(requestKey(ownerKey, requesterKey), { channelId, messageId });
}

export function getDmMessage(ownerKey: string, requesterKey: string): { channelId: string; messageId: string } | null {
  return borrowDmMessages.get(requestKey(ownerKey, requesterKey)) ?? null;
}

// ─── Session borrows ──────────────────────────────────────────────────────────
export function setSessionBorrow(requesterKey: string, ownerKey: string, charName: string): void {
  sessionBorrows.set(requesterKey, { ownerKey, charName });
}

export function getSessionBorrow(requesterKey: string): { ownerKey: string; charName: string } | null {
  return sessionBorrows.get(requesterKey) ?? null;
}

export function clearSessionBorrows(): void {
  sessionBorrows.clear();
  borrowDmMessages.clear();
}

export function canUseCharacter(requesterKey: string, ownerKey: string, charName: string): boolean {
  if (requesterKey === ownerKey) return true;
  const char = getCharacterByName(ownerKey, charName);
  if (char?.sharedWith?.includes(requesterKey)) return true;
  const borrow = getSessionBorrow(requesterKey);
  if (borrow && borrow.ownerKey === ownerKey && borrow.charName.toLowerCase() === charName.toLowerCase()) return true;
  return false;
}

export function clearSessionBorrowForUser(userKey: string): void {
  sessionBorrows.delete(userKey);
}

// ─── DM notifications ─────────────────────────────────────────────────────────
export async function sendBorrowRequestDM(
  client: Client,
  ownerDiscordId: string,
  requesterDisplayName: string,
  ownerKey: string,
  requesterKey: string,
  charName: string,
  charClass: string,
  charLevel: number,
  fallbackChannel?: TextChannel
): Promise<void> {
  const content = `🔔 **Borrow Request**\n**${requesterDisplayName}** wants to borrow **${charName}** (${charClass} · Lv${charLevel}) for tonight's TG.`;

  const acceptBtn = new ButtonBuilder()
    .setCustomId(`borrow_accept:${ownerKey}:${requesterKey}`)
    .setLabel("✅ Accept")
    .setStyle(ButtonStyle.Success);

  const declineBtn = new ButtonBuilder()
    .setCustomId(`borrow_decline:${ownerKey}:${requesterKey}`)
    .setLabel("❌ Decline")
    .setStyle(ButtonStyle.Danger);

  const row = new ActionRowBuilder<ButtonBuilder>().addComponents(acceptBtn, declineBtn);

  try {
    const ownerUser = await client.users.fetch(ownerDiscordId);
    const dm        = await ownerUser.createDM();
    const msg       = await dm.send({ content, components: [row] });
    storeDmMessage(ownerKey, requesterKey, dm.id, msg.id);
  } catch {
    if (fallbackChannel) {
      await fallbackChannel.send({
        content: `<@${ownerDiscordId}> ${content}\nUse \`/tg char accept ${requesterKey}\` or \`/tg char decline ${requesterKey}\`.`,
      });
    }
  }
}

export async function updateBorrowDM(
  client: Client,
  ownerKey: string,
  requesterKey: string,
  accepted: boolean
): Promise<void> {
  const dm = getDmMessage(ownerKey, requesterKey);
  if (!dm) return;
  try {
    const channel = await client.channels.fetch(dm.channelId) as any;
    const message = await channel.messages.fetch(dm.messageId);
    const status  = accepted ? "✅ Accepted" : "❌ Declined";
    await message.edit({ content: `${message.content}\n\n*${status}*`, components: [] });
  } catch {}
}

// ─── Effective character resolution ──────────────────────────────────────────
export function getEffectiveCharacter(userKey: string): { char: any; borrowedFrom: string | null } {
  const { getActiveCharacter, getCharacterByName: getChar } = require("./characters");

  // 1. Session borrow (temporary, resets on poll start)
  const borrow = getSessionBorrow(userKey);
  if (borrow) {
    const char = getChar(borrow.ownerKey, borrow.charName);
    if (char) return { char, borrowedFrom: borrow.ownerKey };
  }

  // 2. Persistent preference (survives restarts and poll resets)
  const pref = getPersistentPreference(userKey);
  console.log(`[getEffectiveCharacter] userKey=${userKey} sessionBorrow=${JSON.stringify(borrow)} pref=${JSON.stringify(pref)}`);
  if (pref) {
    const char = getChar(pref.ownerKey, pref.charName);
    if (char) return { char, borrowedFrom: pref.ownerKey };
    clearPersistentPreference(userKey);
  }

  // 3. Own active character
  const char = getActiveCharacter(userKey);
  return { char: char ?? null, borrowedFrom: null };
}