export const WEAPON_SLOTS = ["main", "off", "back"] as const;
export type WeaponSlot = typeof WEAPON_SLOTS[number];

export const WEAPON_CATEGORIES = [
  "axe",
  "crossbow",
  "dagger",
  "halberd",
  "hammer",
  "quiver",
  "scythe",
  "shield",
  "spear",
  "spellbook",
  "staff",
  "sword",
  "wand",
] as const;
export type WeaponCategory = typeof WEAPON_CATEGORIES[number];

/** Attachment transform profile. Gameplay weapon type remains `category`. */
export const WEAPON_GRIPS = [
  "upright",
  "dagger",
  "staff",
  "wand",
  "crossbow",
  "polearm",
  "prop",
  "back",
] as const;
export type WeaponGrip = typeof WEAPON_GRIPS[number];

export interface WeaponCatalogEntry {
  id: `cc/${string}`;
  label: string;
  category: WeaponCategory;
  allowedSlots: readonly WeaponSlot[];
  grip: WeaponGrip;
  sourceFile: `${string}.glb`;
  optimizedFile: `${string}-uastc.glb` | null;
}

export const CLAUDECRAFT_WEAPON_CATALOG = [
  { id: "cc/adv_axe_1handed", label: "Adventurer One-Handed Axe", category: "axe", allowedSlots: ["main", "off"], grip: "upright", sourceFile: "adv_axe_1handed.glb", optimizedFile: null },
  { id: "cc/adv_axe_2handed", label: "Adventurer Two-Handed Axe", category: "axe", allowedSlots: ["main"], grip: "upright", sourceFile: "adv_axe_2handed.glb", optimizedFile: null },
  { id: "cc/adv_dagger", label: "Adventurer Dagger", category: "dagger", allowedSlots: ["main", "off"], grip: "dagger", sourceFile: "adv_dagger.glb", optimizedFile: "adv_dagger-uastc.glb" },
  { id: "cc/adv_druid_staff", label: "Druid Staff", category: "staff", allowedSlots: ["main"], grip: "staff", sourceFile: "adv_druid_staff.glb", optimizedFile: "adv_druid_staff-uastc.glb" },
  { id: "cc/adv_staff", label: "Adventurer Staff", category: "staff", allowedSlots: ["main"], grip: "staff", sourceFile: "adv_staff.glb", optimizedFile: null },
  { id: "cc/adv_sword_1handed", label: "Adventurer One-Handed Sword", category: "sword", allowedSlots: ["main", "off"], grip: "upright", sourceFile: "adv_sword_1handed.glb", optimizedFile: "adv_sword_1handed-uastc.glb" },
  { id: "cc/adv_sword_2handed", label: "Adventurer Two-Handed Sword", category: "sword", allowedSlots: ["main"], grip: "upright", sourceFile: "adv_sword_2handed.glb", optimizedFile: null },
  { id: "cc/adv_sword_2handed_color", label: "Adventurer Colored Greatsword", category: "sword", allowedSlots: ["main"], grip: "upright", sourceFile: "adv_sword_2handed_color.glb", optimizedFile: null },
  { id: "cc/adv_wand", label: "Adventurer Wand", category: "wand", allowedSlots: ["main"], grip: "wand", sourceFile: "adv_wand.glb", optimizedFile: "adv_wand-uastc.glb" },

  { id: "cc/axe_1handed", label: "One-Handed Axe", category: "axe", allowedSlots: ["main", "off"], grip: "upright", sourceFile: "axe_1handed.glb", optimizedFile: null },
  { id: "cc/axe_2handed", label: "Two-Handed Axe", category: "axe", allowedSlots: ["main"], grip: "upright", sourceFile: "axe_2handed.glb", optimizedFile: null },
  { id: "cc/axe_a", label: "Axe A", category: "axe", allowedSlots: ["main"], grip: "upright", sourceFile: "axe_a.glb", optimizedFile: null },
  { id: "cc/axe_b", label: "Axe B", category: "axe", allowedSlots: ["main"], grip: "upright", sourceFile: "axe_b.glb", optimizedFile: null },
  { id: "cc/axe_c", label: "Axe C", category: "axe", allowedSlots: ["main"], grip: "upright", sourceFile: "axe_c.glb", optimizedFile: null },
  { id: "cc/axe_d", label: "Axe D", category: "axe", allowedSlots: ["main"], grip: "upright", sourceFile: "axe_d.glb", optimizedFile: null },

  { id: "cc/crossbow_1handed", label: "One-Handed Crossbow", category: "crossbow", allowedSlots: ["main"], grip: "crossbow", sourceFile: "crossbow_1handed.glb", optimizedFile: null },
  { id: "cc/crossbow_2handed", label: "Two-Handed Crossbow", category: "crossbow", allowedSlots: ["main"], grip: "crossbow", sourceFile: "crossbow_2handed.glb", optimizedFile: "crossbow_2handed-uastc.glb" },

  { id: "cc/dagger", label: "Dagger", category: "dagger", allowedSlots: ["main", "off"], grip: "dagger", sourceFile: "dagger.glb", optimizedFile: null },
  { id: "cc/dagger_a", label: "Dagger A", category: "dagger", allowedSlots: ["main", "off"], grip: "dagger", sourceFile: "dagger_a.glb", optimizedFile: null },
  { id: "cc/dagger_b", label: "Dagger B", category: "dagger", allowedSlots: ["main", "off"], grip: "dagger", sourceFile: "dagger_b.glb", optimizedFile: null },
  { id: "cc/dagger_c", label: "Dagger C", category: "dagger", allowedSlots: ["main", "off"], grip: "dagger", sourceFile: "dagger_c.glb", optimizedFile: null },

  { id: "cc/halberd", label: "Halberd", category: "halberd", allowedSlots: ["main"], grip: "polearm", sourceFile: "halberd.glb", optimizedFile: null },

  { id: "cc/hammer_a", label: "Sunward Mace", category: "hammer", allowedSlots: ["main"], grip: "upright", sourceFile: "hammer_a.glb", optimizedFile: null },
  { id: "cc/hammer_b", label: "Hammer B", category: "hammer", allowedSlots: ["main"], grip: "upright", sourceFile: "hammer_b.glb", optimizedFile: null },
  { id: "cc/hammer_c", label: "Hammer C", category: "hammer", allowedSlots: ["main"], grip: "upright", sourceFile: "hammer_c.glb", optimizedFile: null },
  { id: "cc/hammer_d", label: "Hammer D", category: "hammer", allowedSlots: ["main"], grip: "upright", sourceFile: "hammer_d.glb", optimizedFile: null },

  { id: "cc/quiver", label: "Quiver", category: "quiver", allowedSlots: ["back"], grip: "back", sourceFile: "quiver.glb", optimizedFile: null },
  { id: "cc/scythe", label: "Scythe", category: "scythe", allowedSlots: ["main"], grip: "polearm", sourceFile: "scythe.glb", optimizedFile: null },

  { id: "cc/shield_badge", label: "Badge Shield", category: "shield", allowedSlots: ["off"], grip: "prop", sourceFile: "shield_badge.glb", optimizedFile: "shield_badge-uastc.glb" },
  { id: "cc/shield_round", label: "Round Shield", category: "shield", allowedSlots: ["off"], grip: "prop", sourceFile: "shield_round.glb", optimizedFile: null },
  { id: "cc/shield_square", label: "Square Shield", category: "shield", allowedSlots: ["off"], grip: "prop", sourceFile: "shield_square.glb", optimizedFile: null },

  { id: "cc/skeleton_axe", label: "Skeleton Axe", category: "axe", allowedSlots: ["main"], grip: "upright", sourceFile: "skeleton_axe.glb", optimizedFile: null },
  { id: "cc/skeleton_blade", label: "Skeleton Blade", category: "sword", allowedSlots: ["main"], grip: "upright", sourceFile: "skeleton_blade.glb", optimizedFile: null },
  { id: "cc/skeleton_crossbow", label: "Skeleton Crossbow", category: "crossbow", allowedSlots: ["main"], grip: "crossbow", sourceFile: "skeleton_crossbow.glb", optimizedFile: null },
  { id: "cc/skeleton_shield_large_a", label: "Skeleton Tower Shield", category: "shield", allowedSlots: ["off"], grip: "prop", sourceFile: "skeleton_shield_large_a.glb", optimizedFile: null },
  { id: "cc/skeleton_staff", label: "Skeleton Staff", category: "staff", allowedSlots: ["main"], grip: "staff", sourceFile: "skeleton_staff.glb", optimizedFile: null },

  { id: "cc/spear_a", label: "Spear A", category: "spear", allowedSlots: ["main"], grip: "polearm", sourceFile: "spear_a.glb", optimizedFile: null },
  { id: "cc/spellbook_open", label: "Open Spellbook", category: "spellbook", allowedSlots: ["off"], grip: "prop", sourceFile: "spellbook_open.glb", optimizedFile: "spellbook_open-uastc.glb" },

  { id: "cc/staff", label: "Staff", category: "staff", allowedSlots: ["main"], grip: "staff", sourceFile: "staff.glb", optimizedFile: null },
  { id: "cc/staff_a", label: "Staff A", category: "staff", allowedSlots: ["main"], grip: "staff", sourceFile: "staff_a.glb", optimizedFile: null },
  { id: "cc/staff_b", label: "Staff B", category: "staff", allowedSlots: ["main"], grip: "staff", sourceFile: "staff_b.glb", optimizedFile: null },
  { id: "cc/staff_c", label: "Staff C", category: "staff", allowedSlots: ["main"], grip: "staff", sourceFile: "staff_c.glb", optimizedFile: null },
  { id: "cc/staff_d", label: "Staff D", category: "staff", allowedSlots: ["main"], grip: "staff", sourceFile: "staff_d.glb", optimizedFile: null },

  { id: "cc/sword_1handed", label: "One-Handed Sword", category: "sword", allowedSlots: ["main", "off"], grip: "upright", sourceFile: "sword_1handed.glb", optimizedFile: null },
  { id: "cc/sword_2handed", label: "Two-Handed Sword", category: "sword", allowedSlots: ["main"], grip: "upright", sourceFile: "sword_2handed.glb", optimizedFile: null },
  { id: "cc/sword_a", label: "Sword A", category: "sword", allowedSlots: ["main"], grip: "upright", sourceFile: "sword_a.glb", optimizedFile: null },
  { id: "cc/sword_b", label: "Sword B", category: "sword", allowedSlots: ["main"], grip: "upright", sourceFile: "sword_b.glb", optimizedFile: null },
  { id: "cc/sword_c", label: "Sword C", category: "sword", allowedSlots: ["main"], grip: "upright", sourceFile: "sword_c.glb", optimizedFile: null },
  { id: "cc/sword_d", label: "Sword D", category: "sword", allowedSlots: ["main"], grip: "upright", sourceFile: "sword_d.glb", optimizedFile: null },
  { id: "cc/sword_e", label: "Sword E", category: "sword", allowedSlots: ["main"], grip: "upright", sourceFile: "sword_e.glb", optimizedFile: null },
  { id: "cc/sword_f", label: "Sword F", category: "sword", allowedSlots: ["main"], grip: "upright", sourceFile: "sword_f.glb", optimizedFile: null },
  { id: "cc/sword_g", label: "Sword G", category: "sword", allowedSlots: ["main"], grip: "upright", sourceFile: "sword_g.glb", optimizedFile: null },

  { id: "cc/wand", label: "Wand", category: "wand", allowedSlots: ["main"], grip: "wand", sourceFile: "wand.glb", optimizedFile: null },
  { id: "cc/wand_a", label: "Wand A", category: "wand", allowedSlots: ["main"], grip: "wand", sourceFile: "wand_a.glb", optimizedFile: null },
  { id: "cc/wand_b", label: "Wand B", category: "wand", allowedSlots: ["main"], grip: "wand", sourceFile: "wand_b.glb", optimizedFile: null },
] as const satisfies readonly WeaponCatalogEntry[];

export type WeaponCatalogId = typeof CLAUDECRAFT_WEAPON_CATALOG[number]["id"];
export type WeaponDefinition = typeof CLAUDECRAFT_WEAPON_CATALOG[number];
export type LegacyWeaponModelId = keyof typeof LEGACY_WEAPON_ID_ALIASES;
export type CharacterBackPropModelId = "cc/quiver";
export type CharacterWeaponModelId = Exclude<WeaponCatalogId, CharacterBackPropModelId>;
export type CharacterWeaponGrip = Exclude<WeaponGrip, "back">;
export type CharacterWeaponCategory = Exclude<WeaponCategory, "quiver">;

export interface CharacterHeldItemVisual {
  modelId: CharacterWeaponModelId;
  grip: CharacterWeaponGrip;
}

const WEAPON_CATALOG_BY_ID = Object.fromEntries(
  CLAUDECRAFT_WEAPON_CATALOG.map((entry) => [entry.id, entry]),
) as Record<WeaponCatalogId, WeaponDefinition>;

export const LEGACY_WEAPON_ID_ALIASES = {
  "druid-staff": "cc/adv_druid_staff",
  sword: "cc/adv_sword_1handed",
  shield: "cc/shield_badge",
  wand: "cc/adv_wand",
  spellbook: "cc/spellbook_open",
} as const satisfies Record<string, WeaponCatalogId>;

export const CHARACTER_BACK_PROP_MODEL_IDS = ["cc/quiver"] as const satisfies readonly CharacterBackPropModelId[];

export const CHARACTER_WEAPON_MODEL_IDS = CLAUDECRAFT_WEAPON_CATALOG
  .filter((entry) => !(entry.allowedSlots as readonly WeaponSlot[]).includes("back"))
  .map((entry) => entry.id) as CharacterWeaponModelId[];

export const TWO_HANDED_WEAPON_IDS = [
  "cc/adv_axe_2handed",
  "cc/adv_druid_staff",
  "cc/adv_staff",
  "cc/adv_sword_2handed",
  "cc/adv_sword_2handed_color",
  "cc/axe_2handed",
  "cc/crossbow_2handed",
  "cc/halberd",
  "cc/scythe",
  "cc/skeleton_crossbow",
  "cc/skeleton_staff",
  "cc/spear_a",
  "cc/staff",
  "cc/staff_a",
  "cc/staff_b",
  "cc/staff_c",
  "cc/staff_d",
  "cc/sword_2handed",
] as const satisfies readonly CharacterWeaponModelId[];

const TWO_HANDED_WEAPON_ID_SET = new Set<CharacterWeaponModelId>(TWO_HANDED_WEAPON_IDS);

function owns(object: object, key: PropertyKey) {
  return Object.prototype.hasOwnProperty.call(object, key);
}

export function isWeaponCatalogId(value: unknown): value is WeaponCatalogId {
  return typeof value === "string" && owns(WEAPON_CATALOG_BY_ID, value);
}

export function resolveWeaponCatalogId(value: unknown): WeaponCatalogId | null {
  if (isWeaponCatalogId(value)) return value;
  if (typeof value !== "string" || !owns(LEGACY_WEAPON_ID_ALIASES, value)) return null;
  return LEGACY_WEAPON_ID_ALIASES[value as LegacyWeaponModelId];
}

export function resolveCharacterWeaponModelId(value: unknown): CharacterWeaponModelId | null {
  const id = resolveWeaponCatalogId(value);
  return id && id !== "cc/quiver" ? id : null;
}

export function weaponCatalogEntry(id: WeaponCatalogId) {
  return WEAPON_CATALOG_BY_ID[id];
}

export const weaponDefinition = weaponCatalogEntry;

export function weaponDefinitionsForSlot(slot: WeaponSlot): WeaponDefinition[] {
  return CLAUDECRAFT_WEAPON_CATALOG
    .filter((entry) => (entry.allowedSlots as readonly WeaponSlot[]).includes(slot));
}

export function heldItemForModel(modelId: CharacterWeaponModelId): CharacterHeldItemVisual {
  const definition = weaponDefinition(modelId);
  return { modelId, grip: definition.grip as CharacterWeaponGrip };
}

export function weaponUsesBothHands(modelId: CharacterWeaponModelId) {
  return TWO_HANDED_WEAPON_ID_SET.has(modelId);
}

export function weaponSupportsSlot(id: WeaponCatalogId, slot: WeaponSlot) {
  return (weaponCatalogEntry(id).allowedSlots as readonly WeaponSlot[]).includes(slot);
}

export function weaponIdsForSlot(slot: WeaponSlot): WeaponCatalogId[] {
  return weaponDefinitionsForSlot(slot).map((entry) => entry.id);
}

export function weaponIdsForCategory(category: WeaponCategory): WeaponCatalogId[] {
  return CLAUDECRAFT_WEAPON_CATALOG
    .filter((entry) => entry.category === category)
    .map((entry) => entry.id);
}

export function weaponIdsForGrip(grip: WeaponGrip): WeaponCatalogId[] {
  return CLAUDECRAFT_WEAPON_CATALOG
    .filter((entry) => entry.grip === grip)
    .map((entry) => entry.id);
}

export function preferredWeaponAssetFile(id: WeaponCatalogId, optimizedAssets = true) {
  const entry = weaponCatalogEntry(id);
  return optimizedAssets && entry.optimizedFile ? entry.optimizedFile : entry.sourceFile;
}
