import i18n from "@/i18n";
import type { ActivityLog, UserSummary } from "./audit-log.types";

export function safeString(v: unknown): string {
  if (v === null || v === undefined) return "";
  if (typeof v === "string") return v;
  try {
    return JSON.stringify(v);
  } catch {
    return String(v);
  }
}

export function toTitleCase(input: string): string {
  return input
    .split(" ")
    .filter(Boolean)
    .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
    .join(" ");
}

export function humanizeKey(raw: string): string {
  if (!raw) return "";
  const normalized = raw
    .replace(/([a-z])([A-Z])/g, "$1 $2")
    .replace(/[:_.-]+/g, " ")
    .trim()
    .toLowerCase();
  return toTitleCase(normalized);
}

export function asName(value: unknown): string {
  if (!value) return "-";
  if (typeof value === "string") return value;
  const obj = value as Record<string, unknown>;
  if (typeof obj?.en === "string" && (obj.en as string).trim()) return obj.en as string;
  if (typeof obj?.am === "string" && (obj.am as string).trim()) return obj.am as string;
  return "-";
}

export function unique(values: string[]): string[] {
  return Array.from(new Set(values.filter(Boolean)));
}

export function mapRoleLabel(roleKey?: string): string {
  if (!roleKey) return "Role";
  return humanizeKey(roleKey);
}

export function mapPermissionLabel(permission: unknown): string {
  const p = permission as Record<string, unknown>;
  const key = typeof p?.key === "string" ? p.key : "";
  const named = asName(p?.name);
  if (named !== "-") return named;
  if (!key) return "Permission";
  return humanizeKey(key);
}

export function buildUserSummary(user: unknown): UserSummary {
  const u = user as Record<string, unknown>;
  const employees = Array.isArray(u?.employee) ? u.employee : [];

  return {
    name: asName(u?.name),
    email: typeof u?.email === "string" ? u.email : "-",
    phone: typeof u?.phoneNumber === "string" ? u.phoneNumber : "-",
    status: humanizeKey(typeof u?.status === "string" ? u.status : ""),
    userType: humanizeKey(typeof u?.userType === "string" ? u.userType : ""),
    roles: unique(
      (Array.isArray(u?.roles) ? u.roles : []).map((r: unknown) => {
        const role = r as Record<string, unknown>;
        return mapRoleLabel(typeof role?.key === "string" ? role.key : "");
      }),
    ),
    employeeInfo: employees.map((emp: unknown, empIndex: number) => {
      const e = emp as Record<string, unknown>;
      const positions = Array.isArray(e?.positions) ? e.positions : [];
      return {
        unit:
          asName((e?.unit as Record<string, unknown>)?.name) !== "-"
            ? asName((e?.unit as Record<string, unknown>)?.name)
            : `Unit ${empIndex + 1}`,
        positions: positions.map((pos: unknown) => {
          const p = pos as Record<string, unknown>;
          return {
            name: asName(p?.name),
            rank: typeof p?.rank === "number" ? p.rank : "-",
            permissions: unique(
              (Array.isArray(p?.permissions) ? p.permissions : []).map(
                (perm: unknown) => mapPermissionLabel(perm),
              ),
            ),
          };
        }),
      };
    }),
    registrationComplete: Boolean(u?.hasFinishedRegistration),
    passwordSet: Boolean(u?.hasSetPassword),
  };
}

export function summarizeValue(value: unknown): string {
  if (value === null || value === undefined) return i18n.t("common.None");
  if (
    typeof value === "string" ||
    typeof value === "number" ||
    typeof value === "boolean"
  ) {
    return String(value);
  }
  if (Array.isArray(value)) {
    return i18n.t("auditLogShared.modal.itemsCount", { count: value.length });
  }
  return i18n.t("auditLogShared.modal.updatedDetails");
}

export function getValueType(v: unknown): string {
  if (Array.isArray(v)) return "array";
  if (v === null) return "null";
  return typeof v;
}

export function collectDiffPaths(
  from: unknown,
  to: unknown,
  prefix = "",
  out: string[] = [],
  depth = 0,
): string[] {
  if (out.length >= 40 || depth > 8) return out;

  const fromType = getValueType(from);
  const toType = getValueType(to);

  if (fromType !== toType) {
    out.push(prefix || "(root)");
    return out;
  }

  if (
    fromType === "string" ||
    fromType === "number" ||
    fromType === "boolean" ||
    fromType === "undefined" ||
    fromType === "null"
  ) {
    if (from !== to) out.push(prefix || "(root)");
    return out;
  }

  if (fromType === "array") {
    const fromArr = Array.isArray(from) ? from : [];
    const toArr = Array.isArray(to) ? to : [];

    if (fromArr.length !== toArr.length)
      out.push(`${prefix || "(root)"}.length`);

    const maxLen = Math.min(Math.max(fromArr.length, toArr.length), 20);
    for (let i = 0; i < maxLen; i++) {
      collectDiffPaths(fromArr[i], toArr[i], `${prefix}[${i}]`, out, depth + 1);
      if (out.length >= 40) break;
    }
    return out;
  }

  const fromObj =
    from && typeof from === "object" ? (from as Record<string, unknown>) : {};
  const toObj =
    to && typeof to === "object" ? (to as Record<string, unknown>) : {};
  const keys = Array.from(
    new Set([...Object.keys(fromObj), ...Object.keys(toObj)]),
  );

  for (const key of keys) {
    const next = prefix ? `${prefix}.${key}` : key;
    collectDiffPaths(fromObj[key], toObj[key], next, out, depth + 1);
    if (out.length >= 40) break;
  }

  return out;
}

type ChangeItem = { field?: string; to?: unknown; from?: unknown };

export function pickFileInfoFromChanges(
  changes?: ChangeItem[],
): unknown {
  if (!changes?.length) return undefined;
  const fileInfoChange = changes.find((c) => c?.field === "fileInfo");
  return fileInfoChange?.to || fileInfoChange?.from;
}

export function pickResourceNameFromChanges(
  changes?: ChangeItem[],
): string | undefined {
  const fileInfo = pickFileInfoFromChanges(changes) as Record<string, unknown> | undefined;
  const fileName =
    fileInfo?.originalname || fileInfo?.fileName || fileInfo?.contentType;
  return fileName ? String(fileName) : undefined;
}

export function pickResourceName(
  item: unknown,
  changes?: ChangeItem[],
): string | undefined {
  const fromChanges = pickResourceNameFromChanges(changes);
  if (fromChanges) return fromChanges;

  const it = item as Record<string, unknown>;
  const payload = it?.payload;
  if (payload && typeof payload === "object") {
    const p = payload as Record<string, unknown>;
    if (typeof p?.entity === "string" && p.entity.trim()) return p.entity;
    if (typeof p?.id === "string" && p.id.trim()) return p.id;
  }

  return undefined;
}

export function extractActor(u: unknown): ActivityLog["performedBy"] | null {
  if (!u || typeof u !== "object") return null;
  const user = u as Record<string, unknown>;

  const email = typeof user?.email === "string" ? user.email : undefined;
  const name =
    typeof user?.name === "string"
      ? user.name
      : typeof (user?.name as Record<string, unknown>)?.en === "string"
        ? (user.name as Record<string, unknown>).en as string
        : typeof (user?.name as Record<string, unknown>)?.am === "string"
          ? (user.name as Record<string, unknown>).am as string
          : typeof user?.username === "string"
            ? user.username
            : undefined;

  if (!name && !email) return null;

  return {
    name: name || (email ? email.split("@")[0] : "System"),
    email: email || "system@audit",
  };
}

export function pickPerformedBy(
  item: unknown,
  changes?: ChangeItem[],
): ActivityLog["performedBy"] {
  if (changes?.length) {
    const userInfoChange = changes.find((c) => c?.field === "userInfo");
    const fromChanges = extractActor(userInfoChange?.to || userInfoChange?.from);
    if (fromChanges) return fromChanges;
  }

  const it = item as Record<string, unknown>;
  const fromAuditLog = extractActor((it?.auditLog as Record<string, unknown>)?.user);
  if (fromAuditLog) return fromAuditLog;

  return { name: "System", email: "system@audit" };
}

export function mapQueryMethodToStatus(_method?: string): ActivityLog["status"] {
  return "success";
}

export function mapQueryMethodToSeverity(method?: string): ActivityLog["severity"] {
  switch ((method || "").toUpperCase()) {
    case "DELETE":
      return "high";
    case "INSERT":
    case "INSERT_CHILD":
      return "medium";
    case "UPDATE":
      return "low";
    default:
      return "low";
  }
}

export function buildDescription(entityName?: string, queryMethod?: string): string {
  const e = entityName || "entity";
  const m = (queryMethod || "ACTION").toUpperCase();
  if (m === "UPDATE") return `Updated ${e}`;
  if (m === "INSERT") return `Created ${e}`;
  if (m === "INSERT_CHILD") return `Created related ${e}`;
  if (m === "DELETE") return `Deleted ${e}`;
  return `${m} on ${e}`;
}

export function buildDetails(
  changes?: ChangeItem[],
  payload?: unknown,
  queryMethod?: string,
  entityName?: string,
): string | undefined {
  if (!changes?.length) {
    if (payload && typeof payload === "object") {
      const p = payload as Record<string, unknown>;
      const payloadEntity = typeof p?.entity === "string" ? p.entity : entityName;
      const payloadId = typeof p?.id === "string" ? p.id : undefined;

      if (payloadEntity || payloadId) {
        return `${(queryMethod || "ACTION").toUpperCase()} ${
          payloadEntity || "entity"
        }${payloadId ? ` (id: ${payloadId})` : ""}`;
      }
    }

    return undefined;
  }

  const lines = changes.slice(0, 8).map((c) => {
    const field = c?.field || "field";

    if (field === "userInfo") {
      const from = c?.from as Record<string, unknown> | undefined;
      const to = c?.to as Record<string, unknown> | undefined;
      const fromEmail = from?.email ? String(from.email) : "-";
      const toEmail = to?.email ? String(to.email) : "-";
      const fromName = asName((from?.name as Record<string, unknown>)?.en ?? from?.name);
      const toName = asName((to?.name as Record<string, unknown>)?.en ?? to?.name);
      return `userInfo: ${fromName} (${fromEmail}) -> ${toName} (${toEmail})`;
    }

    const fromStr = c?.from === null ? "null" : safeString(c?.from);
    const toStr = c?.to === null ? "null" : safeString(c?.to);
    return `${field}: ${fromStr} -> ${toStr}`;
  });

  const more = changes.length > 8 ? `\n... +${changes.length - 8} more` : "";
  return lines.join("\n") + more;
}
