import type { Client, PermissionString } from "discord.js-selfbot-v13";
import { config } from "../config.js";
import { createChildLogger } from "../logger.js";
import { createModerationAction } from "./messageStore.js";
import type { MessageRecord } from "./types.js";

const logger = createChildLogger("auto-delete-manager");

const parseStringList = (value?: string | null): string[] => {
  if (!value) return [];
  try {
    const parsed = JSON.parse(value) as unknown;
    return Array.isArray(parsed)
      ? parsed.filter((item): item is string => typeof item === "string")
      : [];
  } catch {
    return value
      .split(",")
      .map((item) => item.trim())
      .filter(Boolean);
  }
};

/** Derive severity from legacy messages that lack structured AI fields. */
function deriveSeverity(msg: MessageRecord): string {
  if (msg.ai_severity) return msg.ai_severity;
  const score = msg.ai_confidence ?? msg.ai_moderation_score ?? 0;
  if (msg.ai_status === "flagged")
    return score >= 0.9 ? "critical" : score >= 0.7 ? "high" : "medium";
  if (msg.ai_status === "warn") return score >= 0.6 ? "medium" : "low";
  return "none";
}

/** Derive recommended action from legacy messages that lack structured AI fields. */
function deriveRecommendedAction(msg: MessageRecord): string {
  if (msg.ai_recommended_action) return msg.ai_recommended_action;
  const severity = deriveSeverity(msg);
  if (
    msg.ai_status === "flagged" &&
    (severity === "critical" || severity === "high")
  )
    return "delete";
  if (msg.ai_status === "flagged") return "review";
  if (msg.ai_status === "warn") return "warn";
  return "none";
}

function isAutoDeleteEligible(message: MessageRecord): boolean {
  if (message.ai_status !== "flagged" && message.ai_status !== "warn")
    return false;

  const confidence = message.ai_confidence ?? message.ai_moderation_score ?? 0;
  if (confidence < config.AUTO_DELETE_MIN_CONFIDENCE) {
    logger.info(
      {
        messageId: message.id,
        confidence,
        threshold: config.AUTO_DELETE_MIN_CONFIDENCE,
      },
      "Auto-delete skipped: confidence below threshold",
    );
    return false;
  }

  const severity = deriveSeverity(message);
  const allowedSeverities = (config.AUTO_DELETE_ALLOWED_SEVERITIES || "")
    .split(",")
    .map((s) => s.trim())
    .filter(Boolean);
  if (allowedSeverities.length > 0 && !allowedSeverities.includes(severity)) {
    logger.info(
      { messageId: message.id, severity, allowed: allowedSeverities },
      "Auto-delete skipped: severity not in allowed list",
    );
    return false;
  }

  const recommendedAction = deriveRecommendedAction(message);
  if (recommendedAction !== "delete" && recommendedAction !== "escalate") {
    logger.info(
      { messageId: message.id, recommendedAction },
      "Auto-delete skipped: recommended action is not delete/escalate",
    );
    return false;
  }

  const allowedCategories = parseStringList(
    config.AUTO_DELETE_ALLOWED_CATEGORIES,
  );
  if (allowedCategories.length > 0) {
    const messageCategories = parseStringList(
      message.ai_categories ?? message.ai_moderation_flags,
    );
    const hasAllowedCategory = messageCategories.some((cat) =>
      allowedCategories.includes(cat),
    );
    if (!hasAllowedCategory) {
      logger.info(
        {
          messageId: message.id,
          categories: messageCategories,
          allowed: allowedCategories,
        },
        "Auto-delete skipped: no allowed categories match",
      );
      return false;
    }
  }

  const excludedChannels = parseStringList(
    config.AUTO_DELETE_EXCLUDED_CHANNEL_IDS,
  );
  if (excludedChannels.length > 0) {
    const channelId = message.thread_id ?? message.channel_id;
    if (excludedChannels.includes(channelId)) {
      logger.info(
        { messageId: message.id, channelId },
        "Auto-delete skipped: channel excluded",
      );
      return false;
    }
  }

  const excludedUsers = parseStringList(config.AUTO_DELETE_EXCLUDED_USER_IDS);
  if (excludedUsers.length > 0 && excludedUsers.includes(message.user_id)) {
    logger.info(
      { messageId: message.id, userId: message.user_id },
      "Auto-delete skipped: user excluded",
    );
    return false;
  }

  return true;
}

async function logAutoDeleteAttempt(
  message: MessageRecord,
  result: AutoDeleteResult,
): Promise<void> {
  try {
    await createModerationAction({
      message_id: message.id,
      user_id: message.user_id,
      guild_id: message.guild_id,
      action_type: "delete_message",
      reason: result.reason,
      executed_by: "auto-delete-manager",
      status: result.deleted
        ? "executed"
        : result.reason === "dry_run"
          ? "executed"
          : "failed",
      error: result.reason === "error" ? result.reason : null,
      executed_at:
        result.deleted || result.reason === "dry_run" ? Date.now() : null,
    });
  } catch (error) {
    logger.warn(
      {
        messageId: message.id,
        error: error instanceof Error ? error.message : String(error),
      },
      "Failed to persist auto-delete action log",
    );
  }
}

export interface AutoDeleteResult {
  deleted: boolean;
  skipped: boolean;
  reason: string;
}

function getErrorCode(error: unknown): number | string | undefined {
  if (!error || typeof error !== "object") return undefined;
  const maybeCode = (error as { code?: number | string }).code;
  const maybeStatus = (error as { status?: number | string }).status;
  return maybeCode ?? maybeStatus;
}

function isAlreadyDeletedError(error: unknown): boolean {
  const code = getErrorCode(error);
  return code === 10008 || code === 404 || code === "10008" || code === "404";
}

function hasChannelMessagesApi(channel: unknown): channel is {
  messages: {
    fetch: (id: string) => Promise<{ delete: () => Promise<unknown> }>;
  };
} {
  return Boolean(
    channel &&
      typeof channel === "object" &&
      "messages" in channel &&
      (channel as { messages?: unknown }).messages &&
      typeof (channel as { messages: { fetch?: unknown } }).messages.fetch ===
        "function",
  );
}

function hasPermissionApi(channel: unknown): channel is {
  permissionsFor: (
    member: unknown,
  ) => { has: (permission: string) => boolean } | null;
} {
  return Boolean(
    channel &&
      typeof channel === "object" &&
      "permissionsFor" in channel &&
      typeof (channel as { permissionsFor?: unknown }).permissionsFor ===
        "function",
  );
}

export async function attemptAutoDeleteFlaggedMessage(
  client: Client | undefined,
  message: MessageRecord,
): Promise<AutoDeleteResult> {
  if (!config.AUTO_DELETE_FLAGGED_ENABLED) {
    return { deleted: false, skipped: true, reason: "disabled" };
  }

  if (message.ai_status !== "flagged" && message.ai_status !== "warn") {
    const result = {
      deleted: false,
      skipped: true,
      reason: "not_flagged_or_warn",
    } as AutoDeleteResult;
    await logAutoDeleteAttempt(message, result);
    return result;
  }

  if (!isAutoDeleteEligible(message)) {
    const result = {
      deleted: false,
      skipped: true,
      reason: "not_eligible",
    } as AutoDeleteResult;
    await logAutoDeleteAttempt(message, result);
    return result;
  }

  if (!client?.user?.id) {
    logger.warn(
      { messageId: message.id },
      "Auto-delete skipped: client user missing",
    );
    return { deleted: false, skipped: true, reason: "client_user_missing" };
  }

  try {
    const guild = client.guilds.cache.get(message.guild_id);
    if (!guild) {
      logger.warn(
        { messageId: message.id, guildId: message.guild_id },
        "Auto-delete skipped: guild not found",
      );
      return { deleted: false, skipped: true, reason: "guild_not_found" };
    }

    const channelId = message.thread_id ?? message.channel_id;
    const channel = guild.channels.cache.get(channelId);
    if (!channel) {
      logger.warn(
        { messageId: message.id, channelId },
        "Auto-delete skipped: channel not found",
      );
      return { deleted: false, skipped: true, reason: "channel_not_found" };
    }

    if (!hasPermissionApi(channel) || !hasChannelMessagesApi(channel)) {
      logger.warn(
        { messageId: message.id, channelId },
        "Auto-delete skipped: channel cannot delete messages",
      );
      return { deleted: false, skipped: true, reason: "unsupported_channel" };
    }

    const selfMember = await guild.members.fetch(client.user.id);
    const permissions = channel.permissionsFor(selfMember);
    const canManageMessages =
      permissions?.has("MANAGE_MESSAGES" as PermissionString) ?? false;

    if (!canManageMessages) {
      logger.warn(
        { messageId: message.id, channelId, userId: client.user.id },
        "Auto-delete skipped: current user lacks Manage Messages",
      );
      return {
        deleted: false,
        skipped: true,
        reason: "missing_manage_messages",
      };
    }

    if (config.AUTO_DELETE_FLAGGED_DRY_RUN) {
      const result = {
        deleted: false,
        skipped: true,
        reason: "dry_run",
      } as AutoDeleteResult;
      await logAutoDeleteAttempt(message, result);
      logger.info(
        { messageId: message.id, channelId },
        "Auto-delete dry-run: would delete flagged message",
      );
      return result;
    }

    const discordMessage = await channel.messages.fetch(message.id);
    await discordMessage.delete();

    const result = {
      deleted: true,
      skipped: false,
      reason: "deleted",
    } as AutoDeleteResult;
    await logAutoDeleteAttempt(message, result);
    logger.info(
      { messageId: message.id, channelId },
      "Auto-deleted AI-flagged message",
    );
    return result;
  } catch (error) {
    if (isAlreadyDeletedError(error)) {
      const result = {
        deleted: true,
        skipped: false,
        reason: "already_deleted",
      } as AutoDeleteResult;
      await logAutoDeleteAttempt(message, result);
      logger.info(
        { messageId: message.id, code: getErrorCode(error) },
        "Auto-delete skipped: message already deleted",
      );
      return result;
    }

    const result = {
      deleted: false,
      skipped: true,
      reason: "error",
    } as AutoDeleteResult;
    await logAutoDeleteAttempt(message, result);
    logger.error(
      {
        messageId: message.id,
        error: error instanceof Error ? error.message : String(error),
        code: getErrorCode(error),
      },
      "Auto-delete failed",
    );
    return result;
  }
}
