import requests
import sqlite3
import sys

DB_PATH = "tarkov.db"
API_URL = "https://api.tarkov.dev/graphql"

# Slot nameId patterns that identify what type of mod goes in a slot
SLOT_TYPE_MAP = {
    "mod_muzzle":       "suppressor",
    "mod_scope":        "scope",
    "mod_tactical":     "flashlight",
    "mod_tactical_001": "flashlight",
    "mod_tactical_002": "flashlight",
    "mod_tactical_003": "flashlight",
    "mod_stock":        "stock",
    "mod_stock_000":    "stock",
    "mod_stock_001":    "stock",
    "mod_pistol_grip":  "grip",
    "mod_grip":         "foregrip",
    "mod_foregrip":     "foregrip",
    "mod_magazine":     "magazine",
    "mod_barrel":       "barrel",
    "mod_gas_block":    "gas_block",
    "mod_handguard":    "handguard",
    "mod_launcher":     "launcher",
    "mod_bipod":        "bipod",
}

# Fetch weapons separately due to large slots/allowedItems payload
GRAPHQL_QUERY_WEAPONS = """
{
  weapons: items(types: [gun]) {
    id name shortName weight gridImageLink wikiLink
    properties {
      ... on ItemPropertiesWeapon {
        caliber
        fireRate
        ergonomics
        recoilVertical
        defaultWeight
        slots {
          id
          name
          nameId
          required
          filters {
            allowedItems { id }
          }
        }
      }
    }
  }
}
"""

GRAPHQL_QUERY_GEAR = """
{
  armor: items(types: [armor]) {
    id name shortName weight gridImageLink wikiLink
    properties {
      ... on ItemPropertiesArmor {
        class
        durability
        material { name }
        zones
        armorSlots {
          __typename
          ... on ItemArmorSlotOpen {
            nameId
            zones
            allowedPlates { id }
          }
        }
      }
    }
  }

  helmets: items(types: [helmet]) {
    id name shortName weight gridImageLink wikiLink
    properties {
      ... on ItemPropertiesHelmet {
        class
        durability
        material { name }
        headZones
        deafening
      }
    }
  }

  wearables: items(types: [wearable]) {
    id name shortName weight gridImageLink wikiLink
    properties {
      ... on ItemPropertiesHelmet {
        class
        durability
        material { name }
        headZones
        deafening
      }
    }
  }

  backpacks: items(types: [backpack]) {
    id name shortName weight gridImageLink wikiLink
    properties {
      ... on ItemPropertiesBackpack {
        capacity
        grids { width height }
      }
    }
  }

  rigs: items(types: [rig]) {
    id name shortName weight gridImageLink wikiLink
    properties {
      ... on ItemPropertiesChestRig {
        capacity
        class
        durability
        zones
        armorSlots {
          __typename
          ... on ItemArmorSlotOpen {
            nameId
            zones
            allowedPlates { id }
          }
        }
      }
    }
  }

  plates: items(types: [armorPlate]) {
    id name shortName weight gridImageLink wikiLink
    properties {
      ... on ItemPropertiesArmorAttachment {
        class
        durability
        material { name }
        zones
      }
    }
  }

  suppressors: items(types: [suppressor]) {
    id name shortName weight gridImageLink wikiLink
  }

  mods: items(types: [mods]) {
    id name shortName weight gridImageLink wikiLink
  }
}
"""


def gql(query, label="query"):
    response = requests.post(
        API_URL,
        json={"query": query},
        timeout=90
    )
    response.raise_for_status()
    data = response.json()
    if "errors" in data:
        raise RuntimeError(f"{label} errors: {data['errors']}")
    return data["data"]


def upsert_item(cursor, item_id, name, short_name, category, weight,
                grid_image_url, wiki_url,
                caliber=None, fire_rate=None, ergonomics=None, recoil_vertical=None,
                default_weight=None, armor_class=None, durability=None, material=None,
                zones=None, head_zones=None, deafening=None, capacity=None, mod_type=None):
    cursor.execute("SELECT id FROM gear_items WHERE api_id = ?", (item_id,))
    existing = cursor.fetchone()
    if existing:
        cursor.execute("""
            UPDATE gear_items
            SET name=?, short_name=?, category=?, weight_kg=?, grid_image_url=?, wiki_url=?,
                caliber=?, fire_rate=?, ergonomics=?, recoil_vertical=?, default_weight=?,
                armor_class=?, durability=?, material=?, zones=?, head_zones=?, deafening=?,
                capacity=?, mod_type=?, updated_at=CURRENT_TIMESTAMP
            WHERE api_id=?
        """, (name, short_name, category, weight, grid_image_url, wiki_url,
              caliber, fire_rate, ergonomics, recoil_vertical, default_weight,
              armor_class, durability, material, zones, head_zones, deafening,
              capacity, mod_type, item_id))
        return "updated"
    else:
        cursor.execute("""
            INSERT INTO gear_items
                (id, api_id, name, short_name, category, weight_kg, grid_image_url, wiki_url,
                 caliber, fire_rate, ergonomics, recoil_vertical, default_weight,
                 armor_class, durability, material, zones, head_zones, deafening,
                 capacity, mod_type)
            VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
        """, (item_id, item_id, name, short_name, category, weight, grid_image_url, wiki_url,
              caliber, fire_rate, ergonomics, recoil_vertical, default_weight,
              armor_class, durability, material, zones, head_zones, deafening,
              capacity, mod_type))
        return "inserted"


def import_weapons(conn, weapons):
    cursor = conn.cursor()
    counts = {"inserted": 0, "updated": 0, "skipped": 0}
    # Collect slot data for phase 2
    slot_data = {}  # gun_id -> list of slot dicts

    for w in weapons:
        item_id = w.get("id")
        name = w.get("name")
        if not item_id or not name:
            counts["skipped"] += 1
            continue

        props = w.get("properties") or {}
        result = upsert_item(
            cursor, item_id, name, w.get("shortName"), "gun", w.get("weight"),
            w.get("gridImageLink"), w.get("wikiLink"),
            caliber=props.get("caliber"),
            fire_rate=props.get("fireRate"),
            ergonomics=props.get("ergonomics"),
            recoil_vertical=props.get("recoilVertical"),
            default_weight=props.get("defaultWeight"),
        )
        counts[result] += 1

        slots = props.get("slots") or []
        if slots:
            slot_data[item_id] = slots

    conn.commit()
    return counts, slot_data


def import_armor(conn, items):
    cursor = conn.cursor()
    counts = {"inserted": 0, "updated": 0, "skipped": 0}
    slot_data = {}  # carrier_id -> armorSlots list
    for item in items:
        item_id = item.get("id")
        name = item.get("name")
        if not item_id or not name:
            counts["skipped"] += 1
            continue
        props = item.get("properties") or {}
        material = (props.get("material") or {}).get("name")
        zones = ",".join(props.get("zones") or []) or None
        result = upsert_item(
            cursor, item_id, name, item.get("shortName"), "armor", item.get("weight"),
            item.get("gridImageLink"), item.get("wikiLink"),
            armor_class=props.get("class"),
            durability=props.get("durability"),
            material=material,
            zones=zones,
        )
        counts[result] += 1
        armor_slots = props.get("armorSlots") or []
        if armor_slots:
            slot_data[item_id] = armor_slots
    conn.commit()
    return counts, slot_data


def import_helmets(conn, items):
    cursor = conn.cursor()
    counts = {"inserted": 0, "updated": 0, "skipped": 0}
    for item in items:
        item_id = item.get("id")
        name = item.get("name")
        if not item_id or not name:
            counts["skipped"] += 1
            continue
        props = item.get("properties") or {}
        material = (props.get("material") or {}).get("name")
        head_zones_list = props.get("headZones") or []
        head_zones = ",".join(head_zones_list) or None
        # True helmets cover the top of the head; face masks etc. go to 'headwear'
        category = "helmet" if any("Top of the head" in z for z in head_zones_list) else "headwear"
        result = upsert_item(
            cursor, item_id, name, item.get("shortName"), category, item.get("weight"),
            item.get("gridImageLink"), item.get("wikiLink"),
            armor_class=props.get("class"),
            durability=props.get("durability"),
            material=material,
            head_zones=head_zones,
            deafening=props.get("deafening"),
        )
        counts[result] += 1
    conn.commit()
    return counts


def import_backpacks(conn, items):
    cursor = conn.cursor()
    counts = {"inserted": 0, "updated": 0, "skipped": 0}
    for item in items:
        item_id = item.get("id")
        name = item.get("name")
        if not item_id or not name:
            counts["skipped"] += 1
            continue
        props = item.get("properties") or {}
        capacity = props.get("capacity")
        if capacity is None:
            capacity = sum(
                g.get("width", 0) * g.get("height", 0)
                for g in (props.get("grids") or [])
            ) or None
        result = upsert_item(
            cursor, item_id, name, item.get("shortName"), "backpack", item.get("weight"),
            item.get("gridImageLink"), item.get("wikiLink"),
            capacity=capacity,
        )
        counts[result] += 1
    conn.commit()
    return counts


def import_rigs(conn, items):
    cursor = conn.cursor()
    counts = {"inserted": 0, "updated": 0, "skipped": 0}
    slot_data = {}  # carrier_id -> armorSlots list
    for item in items:
        item_id = item.get("id")
        name = item.get("name")
        if not item_id or not name:
            counts["skipped"] += 1
            continue
        props = item.get("properties") or {}
        zones = ",".join(props.get("zones") or []) or None
        result = upsert_item(
            cursor, item_id, name, item.get("shortName"), "rig", item.get("weight"),
            item.get("gridImageLink"), item.get("wikiLink"),
            armor_class=props.get("class"),
            durability=props.get("durability"),
            capacity=props.get("capacity"),
            zones=zones,
        )
        counts[result] += 1
        armor_slots = props.get("armorSlots") or []
        if armor_slots:
            slot_data[item_id] = armor_slots
    conn.commit()
    return counts, slot_data


def import_plates(conn, items):
    cursor = conn.cursor()
    counts = {"inserted": 0, "updated": 0, "skipped": 0}
    for item in items:
        item_id = item.get("id")
        name = item.get("name")
        if not item_id or not name:
            counts["skipped"] += 1
            continue
        props = item.get("properties") or {}
        material = (props.get("material") or {}).get("name")
        zones = ",".join(props.get("zones") or []) or None
        result = upsert_item(
            cursor, item_id, name, item.get("shortName"), "plate", item.get("weight"),
            item.get("gridImageLink"), item.get("wikiLink"),
            armor_class=props.get("class"),
            durability=props.get("durability"),
            material=material,
            zones=zones,
        )
        counts[result] += 1
    conn.commit()
    return counts


def import_armor_open_slots(conn, carrier_id, armor_slots):
    """
    Insert open plate slots and their compatible plates for a carrier item.
    armor_slots: list of armorSlots from the API (only ItemArmorSlotOpen are relevant).
    """
    cursor = conn.cursor()
    slot_count = 0
    plate_count = 0
    # Build set of known item IDs for fast lookup
    known_ids = {row[0] for row in cursor.execute("SELECT id FROM gear_items").fetchall()}

    for slot in armor_slots:
        if slot.get("__typename") != "ItemArmorSlotOpen":
            continue
        slot_nameid = slot.get("nameId")
        if not slot_nameid:
            continue
        zones = ",".join(slot.get("zones") or []) or None
        cursor.execute("""
            INSERT OR REPLACE INTO armor_open_slots (carrier_id, slot_nameid, zones)
            VALUES (?, ?, ?)
        """, (carrier_id, slot_nameid, zones))
        slot_count += 1

        for plate in (slot.get("allowedPlates") or []):
            plate_id = plate.get("id")
            if not plate_id or plate_id not in known_ids:
                continue
            cursor.execute("""
                INSERT OR IGNORE INTO armor_slot_plates (carrier_id, slot_nameid, plate_id)
                VALUES (?, ?, ?)
            """, (carrier_id, slot_nameid, plate_id))
            plate_count += 1

    return slot_count, plate_count


def import_suppressors(conn, items):
    cursor = conn.cursor()
    counts = {"inserted": 0, "updated": 0, "skipped": 0}
    for item in items:
        item_id = item.get("id")
        name = item.get("name")
        if not item_id or not name:
            counts["skipped"] += 1
            continue
        result = upsert_item(
            cursor, item_id, name, item.get("shortName"), "mod", item.get("weight"),
            item.get("gridImageLink"), item.get("wikiLink"),
            mod_type="suppressor",
        )
        counts[result] += 1
    conn.commit()
    return counts


def import_mods(conn, items):
    """Import generic weapon mods (scopes, grips, stocks, barrels, etc.)"""
    cursor = conn.cursor()
    counts = {"inserted": 0, "updated": 0, "skipped": 0}
    for item in items:
        item_id = item.get("id")
        name = item.get("name")
        if not item_id or not name:
            counts["skipped"] += 1
            continue
        # Don't overwrite suppressors already imported with their mod_type
        cursor.execute("SELECT id FROM gear_items WHERE api_id = ?", (item_id,))
        existing = cursor.fetchone()
        if existing:
            # Only update weight/image if item already exists — don't overwrite mod_type
            cursor.execute("""
                UPDATE gear_items
                SET name=?, short_name=?, weight_kg=?, grid_image_url=?, wiki_url=?,
                    updated_at=CURRENT_TIMESTAMP
                WHERE api_id=?
            """, (name, item.get("shortName"), item.get("weight"),
                  item.get("gridImageLink"), item.get("wikiLink"), item_id))
            counts["updated"] += 1
        else:
            result = upsert_item(
                cursor, item_id, name, item.get("shortName"), "mod", item.get("weight"),
                item.get("gridImageLink"), item.get("wikiLink"),
            )
            counts[result] += 1
    conn.commit()
    return counts


def import_slots(conn, slot_data):
    """
    Phase 2: insert gun_slots and gun_slot_items rows.
    slot_data: { gun_id: [slot_dict, ...] }
    Only inserts gun_slot_items rows where item_id exists in gear_items.
    """
    cursor = conn.cursor()
    slots_inserted = 0
    slot_items_inserted = 0
    slot_items_skipped = 0

    # Build set of known item IDs for fast lookup
    known_ids = {row[0] for row in cursor.execute("SELECT id FROM gear_items").fetchall()}

    for gun_id, slots in slot_data.items():
        for slot in slots:
            slot_id = slot.get("id")
            slot_name = slot.get("name") or ""
            slot_nameid = slot.get("nameId") or ""
            required = 1 if slot.get("required") else 0

            if not slot_id:
                continue

            cursor.execute("""
                INSERT OR REPLACE INTO gun_slots (gun_id, slot_id, slot_name, slot_nameid, required)
                VALUES (?, ?, ?, ?, ?)
            """, (gun_id, slot_id, slot_name, slot_nameid, required))
            slots_inserted += 1

            filters = slot.get("filters") or {}
            allowed_items = filters.get("allowedItems") or []
            for allowed in allowed_items:
                item_id = allowed.get("id")
                if not item_id or item_id not in known_ids:
                    slot_items_skipped += 1
                    continue
                cursor.execute("""
                    INSERT OR IGNORE INTO gun_slot_items (gun_id, slot_id, item_id)
                    VALUES (?, ?, ?)
                """, (gun_id, slot_id, item_id))
                slot_items_inserted += 1

    conn.commit()
    return slots_inserted, slot_items_inserted, slot_items_skipped


def main():
    print("=== OnlyScavs Gear Import ===")

    # Phase 1a: fetch weapons (separate query — large payload)
    print("\nFetching weapons from tarkov.dev...")
    try:
        weapon_data = gql(GRAPHQL_QUERY_WEAPONS, "weapons")
    except Exception as e:
        print(f"ERROR fetching weapons: {e}")
        sys.exit(1)
    weapons = weapon_data.get("weapons", [])
    print(f"  Fetched {len(weapons)} weapons")

    # Phase 1b: fetch other gear
    print("Fetching armor, helmets, backpacks, rigs, suppressors...")
    try:
        gear_data = gql(GRAPHQL_QUERY_GEAR, "gear")
    except Exception as e:
        print(f"ERROR fetching gear: {e}")
        sys.exit(1)

    armor       = gear_data.get("armor", [])
    helmets     = gear_data.get("helmets", [])
    backpacks   = gear_data.get("backpacks", [])
    rigs        = gear_data.get("rigs", [])
    suppressors = gear_data.get("suppressors", [])
    mods        = gear_data.get("mods", [])
    plates      = gear_data.get("plates", [])

    # Wearables: only keep those whose properties resolved to ItemPropertiesHelmet
    # (i.e. they have 'class' or 'headZones' set — not decorative items)
    wearables_raw = gear_data.get("wearables", [])
    # Known helmet IDs from the main helmet query — skip duplicates
    helmet_ids = {h["id"] for h in helmets}
    wearable_helmets = [
        w for w in wearables_raw
        if w["id"] not in helmet_ids
        and isinstance(w.get("properties"), dict)
        and (w["properties"].get("class") or w["properties"].get("headZones"))
    ]
    print(f"  armor={len(armor)}, helmets={len(helmets)}, wearable_helmets={len(wearable_helmets)}, "
          f"backpacks={len(backpacks)}, rigs={len(rigs)}, suppressors={len(suppressors)}, "
          f"mods={len(mods)}, plates={len(plates)})")

    # Phase 2: insert into DB
    conn = sqlite3.connect(DB_PATH)
    conn.execute("PRAGMA foreign_keys = ON")

    try:
        print("\nImporting weapons...")
        wcounts, slot_data = import_weapons(conn, weapons)
        print(f"  guns:        +{wcounts['inserted']} inserted, ~{wcounts['updated']} updated, -{wcounts['skipped']} skipped")

        print("Importing plates (armorPlate items)...")
        c = import_plates(conn, plates)
        print(f"  plates:      +{c['inserted']} inserted, ~{c['updated']} updated, -{c['skipped']} skipped")

        print("Importing armor...")
        c, armor_slot_data = import_armor(conn, armor)
        print(f"  armor:       +{c['inserted']} inserted, ~{c['updated']} updated, -{c['skipped']} skipped")

        print("Importing helmets...")
        c = import_helmets(conn, helmets)
        print(f"  helmets:     +{c['inserted']} inserted, ~{c['updated']} updated, -{c['skipped']} skipped")

        print("Importing wearable helmets (missing from helmet type)...")
        c = import_helmets(conn, wearable_helmets)
        print(f"  wearable helmets: +{c['inserted']} inserted, ~{c['updated']} updated, -{c['skipped']} skipped")

        print("Importing backpacks...")
        c = import_backpacks(conn, backpacks)
        print(f"  backpacks:   +{c['inserted']} inserted, ~{c['updated']} updated, -{c['skipped']} skipped")

        print("Importing rigs...")
        c, rig_slot_data = import_rigs(conn, rigs)
        print(f"  rigs:        +{c['inserted']} inserted, ~{c['updated']} updated, -{c['skipped']} skipped")

        print("Importing suppressors...")
        c = import_suppressors(conn, suppressors)
        print(f"  suppressors: +{c['inserted']} inserted, ~{c['updated']} updated, -{c['skipped']} skipped")

        print("Importing mods (scopes, grips, stocks, barrels, etc.)...")
        c = import_mods(conn, mods)
        print(f"  mods:        +{c['inserted']} inserted, ~{c['updated']} updated, -{c['skipped']} skipped")

        # Phase 3: gun slots (needs all items in DB first)
        print("Importing gun slots and compatibility data...")
        slots_ins, slot_items_ins, slot_items_skip = import_slots(conn, slot_data)
        print(f"  gun_slots:      {slots_ins} rows")
        print(f"  gun_slot_items: {slot_items_ins} rows inserted, {slot_items_skip} skipped (item not in DB)")

        # Phase 3b: armor open slots (needs plates in DB first)
        print("Importing armor open plate slots...")
        total_open_slots = 0
        total_plate_links = 0
        all_carrier_slots = {**armor_slot_data, **rig_slot_data}
        for carrier_id, armor_slots in all_carrier_slots.items():
            sc, pc = import_armor_open_slots(conn, carrier_id, armor_slots)
            total_open_slots += sc
            total_plate_links += pc
        conn.commit()
        print(f"  armor_open_slots: {total_open_slots} rows")
        print(f"  armor_slot_plates: {total_plate_links} rows")

        # Phase 4: classify mod_type for mods based on which slots they appear in
        print("Classifying mod types from slot data...")
        cursor = conn.cursor()
        updated_mod_types = 0
        for slot_nameid, mod_type in SLOT_TYPE_MAP.items():
            result = cursor.execute("""
                UPDATE gear_items
                SET mod_type = ?
                WHERE id IN (
                    SELECT gsi.item_id
                    FROM gun_slot_items gsi
                    JOIN gun_slots gs ON gs.gun_id = gsi.gun_id AND gs.slot_id = gsi.slot_id
                    WHERE gs.slot_nameid = ?
                )
                AND category = 'mod'
                AND (mod_type IS NULL OR mod_type != 'suppressor')
            """, (mod_type, slot_nameid))
            updated_mod_types += result.rowcount
        conn.commit()
        print(f"  mod_type set on {updated_mod_types} mod items")

    except Exception as e:
        conn.rollback()
        print(f"ERROR: Database operation failed — {e}")
        import traceback
        traceback.print_exc()
        sys.exit(1)
    finally:
        conn.close()

    print("\n=== Import complete ===")
    print("Run: SELECT category, COUNT(*) FROM gear_items GROUP BY category;")
    print("  to verify row counts.")


if __name__ == "__main__":
    main()
