#!/usr/bin/env python3
"""
IMPORTADOR DEL CATÁLOGO FRAM 2017
- Sección de vehículos livianos (páginas 3-87): Brand → Model + Motor + Dates + Filters
- Sección de equivalencias (páginas 149-199): Competitor → FRAM mappings
- Filtros: PH/CH = Aceite, CA/PA = Aire, G/P/PS = Combustible, CF/CFA = Cabina
"""

import sqlite3
import re
import pypdf
from pathlib import Path
from collections import defaultdict

DB_PATH = Path(__file__).parent.parent / 'vehicle_database.db'
PDF_PATH = '/tmp/catalogs/fram_2017.pdf'

# Filter type classification by part number prefix
FILTER_PREFIXES = {
    'PH': ('Oil Filters', 'Oil Filter', 'Filtro de Aceite'),
    'CH': ('Oil Filters', 'Oil Filter Cartridge', 'Filtro de Aceite Cartucho'),
    'CA': ('Air Filters', 'Air Filter', 'Filtro de Aire'),
    'PA': ('Air Filters', 'Air Filter', 'Filtro de Aire'),
    'G':  ('Fuel Filters', 'Fuel Filter', 'Filtro de Combustible'),
    'P':  ('Fuel Filters', 'Fuel Filter', 'Filtro de Combustible'),
    'PS': ('Fuel Filters', 'Fuel Filter', 'Filtro de Combustible'),
    'CF': ('Cabin Air Filters', 'Cabin Air Filter', 'Filtro de Cabina'),
    'CFA': ('Cabin Air Filters', 'Cabin Air Filter', 'Filtro de Cabina'),
}

# FRAM part number pattern
FRAM_PART_RE = re.compile(r'\b(CFA?\d[\w-]*|PH\d[\w-]*|CH\d[\w-]*|CA\d[\w-]*|PA\d[\w-]*|PS\d[\w-]*|G\d[\w-]*|P\d[\w-]*)\b')

# Known brands that appear as headers in the FRAM catalog
KNOWN_BRANDS = {
    'ACURA', 'ALEKO', 'ALFA ROMEO', 'ASIA MOTORS', 'ASTON MARTIN', 'AUDI',
    'BEDFORD', 'BENTLEY', 'BMW', 'BUICK', 'CADILLAC', 'CHANA', 'CHERY',
    'CHEVROLET', 'CHRYSLER', 'CITROEN', 'DAEWOO', 'DACIA', 'DAIHATSU',
    'DODGE', 'EAGLE', 'FAW', 'FIAT', 'FORD', 'GALLOPER', 'GEO', 'GEELY',
    'GREAT WALL', 'HONDA', 'HUMMER', 'HYUNDAI', 'INFINITI', 'ISUZU',
    'IVECO', 'JAC', 'JAGUAR', 'JEEP', 'KIA', 'LADA', 'LANCIA', 'LAND ROVER',
    'LEXUS', 'LIFAN', 'LINCOLN', 'LOTUS', 'MAHINDRA', 'MASERATI', 'MAZDA',
    'MERCEDES BENZ', 'MERCURY', 'MG', 'MINI', 'MITSUBISHI', 'NISSAN',
    'OLDSMOBILE', 'OPEL', 'PEUGEOT', 'PLYMOUTH', 'PONTIAC', 'PORSCHE',
    'RAM', 'RENAULT', 'ROVER', 'SAAB', 'SAMSUNG', 'SATURN', 'SCION',
    'SEAT', 'SKODA', 'SMART', 'SSANGYONG', 'SUBARU', 'SUZUKI', 'TATA',
    'TOYOTA', 'TRIUMPH', 'VAUXHALL', 'VOLKSWAGEN', 'VOLVO',
}


def get_db():
    conn = sqlite3.connect(DB_PATH)
    conn.row_factory = sqlite3.Row
    return conn


def ensure_manufacturer(cursor, name, type_='aftermarket', quality='standard', country=None):
    cursor.execute("SELECT id FROM manufacturers WHERE UPPER(name) = UPPER(?)", (name,))
    row = cursor.fetchone()
    if row:
        return row['id']
    cursor.execute(
        "INSERT INTO manufacturers (name, type, quality_tier, country) VALUES (?, ?, ?, ?)",
        (name, type_, quality, country))
    return cursor.lastrowid


def ensure_brand(cursor, name):
    cursor.execute("SELECT id FROM brands WHERE UPPER(name) = UPPER(?)", (name,))
    row = cursor.fetchone()
    if row:
        return row['id']
    cursor.execute("INSERT INTO brands (name) VALUES (?)", (name,))
    return cursor.lastrowid


def ensure_model(cursor, brand_id, name):
    cursor.execute(
        "SELECT id FROM models WHERE brand_id = ? AND UPPER(name) = UPPER(?)",
        (brand_id, name))
    row = cursor.fetchone()
    if row:
        return row['id']
    cursor.execute("INSERT INTO models (brand_id, name) VALUES (?, ?)", (brand_id, name))
    return cursor.lastrowid


def ensure_year(cursor, year):
    cursor.execute("SELECT id FROM years WHERE year = ?", (year,))
    row = cursor.fetchone()
    if row:
        return row['id']
    cursor.execute("INSERT INTO years (year) VALUES (?)", (year,))
    return cursor.lastrowid


def ensure_engine(cursor, name):
    cursor.execute("SELECT id FROM engines WHERE name = ?", (name,))
    row = cursor.fetchone()
    if row:
        return row['id']
    displacement = None
    cylinders = None
    fuel_type = 'gasoline'
    m = re.search(r'(\d+)cc', name)
    if m:
        displacement = int(m.group(1))
    if 'diesel' in name.lower() or 'td' in name.lower() or 'tdi' in name.lower() or 'jtd' in name.lower():
        fuel_type = 'diesel'
    cursor.execute(
        "INSERT INTO engines (name, displacement_cc, cylinders, fuel_type) VALUES (?, ?, ?, ?)",
        (name, displacement, cylinders, fuel_type))
    return cursor.lastrowid


def get_generic_engine(cursor):
    cursor.execute("SELECT id FROM engines WHERE name = 'Generic'")
    row = cursor.fetchone()
    if row:
        return row['id']
    cursor.execute("INSERT INTO engines (name, fuel_type) VALUES ('Generic', 'gasoline')")
    return cursor.lastrowid


def ensure_mye(cursor, model_id, year_id, engine_id=None):
    if engine_id:
        cursor.execute(
            "SELECT id FROM model_year_engine WHERE model_id = ? AND year_id = ? AND engine_id = ?",
            (model_id, year_id, engine_id))
    else:
        cursor.execute(
            "SELECT id FROM model_year_engine WHERE model_id = ? AND year_id = ?",
            (model_id, year_id))
    row = cursor.fetchone()
    if row:
        return row['id']
    if not engine_id:
        engine_id = get_generic_engine(cursor)
    cursor.execute(
        "INSERT INTO model_year_engine (model_id, year_id, engine_id) VALUES (?, ?, ?)",
        (model_id, year_id, engine_id))
    return cursor.lastrowid


def classify_filter(part_number):
    """Classify FRAM filter by part number prefix and return (group_name, name_en, name_es)."""
    pn_upper = part_number.upper()
    # Check longer prefixes first
    for prefix in ['CFA', 'CF', 'PS', 'PH', 'CH', 'CA', 'PA']:
        if pn_upper.startswith(prefix):
            return FILTER_PREFIXES[prefix]
    # Single letter prefixes
    if pn_upper.startswith('G') and re.match(r'^G\d', pn_upper):
        return FILTER_PREFIXES['G']
    if pn_upper.startswith('P') and re.match(r'^P\d', pn_upper):
        return FILTER_PREFIXES['P']
    return None


def get_or_create_group(cursor, group_name):
    """Get group ID by name."""
    cursor.execute("SELECT id FROM part_groups WHERE name = ?", (group_name,))
    row = cursor.fetchone()
    if row:
        return row['id']
    # Find category
    cat_map = {
        'Oil Filters': 'Engine', 'Air Filters': 'Engine',
        'Fuel Filters': 'Fuel & Air', 'Cabin Air Filters': 'Heat & Air Conditioning',
    }
    cat_name = cat_map.get(group_name, 'Engine')
    cursor.execute("SELECT id FROM part_categories WHERE name = ?", (cat_name,))
    cat = cursor.fetchone()
    if not cat:
        return None
    cursor.execute(
        "INSERT INTO part_groups (category_id, name) VALUES (?, ?)",
        (cat['id'], group_name))
    return cursor.lastrowid


def get_or_create_part(cursor, part_number, group_id, name, name_es, description):
    cursor.execute("SELECT id FROM parts WHERE oem_part_number = ?", (part_number,))
    row = cursor.fetchone()
    if row:
        return row['id'], False
    cursor.execute(
        "INSERT INTO parts (oem_part_number, name, name_es, group_id, description) VALUES (?, ?, ?, ?, ?)",
        (part_number, name, name_es, group_id, description))
    return cursor.lastrowid, True


def parse_date_range(date_str):
    """Parse FRAM date range like (03/88 - 09/97) into year range."""
    m = re.match(r'\(?\s*(\d{2})/(\d{2,4})\s*-\s*(\d{2})/(\d{2,4})\s*\)?', date_str)
    if m:
        y1 = int(m.group(2))
        y2 = int(m.group(4))
        if y1 < 100:
            y1 += 2000 if y1 < 50 else 1900
        if y2 < 100:
            y2 += 2000 if y2 < 50 else 1900
        return list(range(y1, y2 + 1))
    # Try single year
    m = re.match(r'\(?\s*(\d{2})/(\d{2,4})\s*-?\s*\)?', date_str)
    if m:
        y = int(m.group(2))
        if y < 100:
            y += 2000 if y < 50 else 1900
        return [y]
    return []


def extract_fram_parts(text):
    """Extract FRAM part numbers from a text string."""
    return FRAM_PART_RE.findall(text)


def parse_vehicle_entries(pdf):
    """Parse vehicle entries from FRAM catalog (light vehicles section)."""
    entries = []
    current_brand = None
    current_model_group = None

    for page_num in range(2, 87):  # Pages 3-87 (0-indexed)
        text = pdf.pages[page_num].extract_text()
        if not text:
            continue

        lines = text.split('\n')
        prev_line = ""

        for line in lines:
            line = line.strip()
            if not line:
                continue

            # Skip headers/footers
            if line.startswith('LIVIANOS') or line.startswith('PESADOS'):
                continue
            if re.match(r'^\d{1,3}$', line):
                continue
            if 'MARCA/CATEGORÍA' in line:
                continue
            # Skip dimension notes
            if re.match(r'^H1=', line) or line.startswith('Parcial') or line.startswith('Panel') or line.startswith('Redondo'):
                continue
            if line.startswith('C/C.') or line.startswith('Unidad Sellada'):
                continue

            # Brand detection
            if line in KNOWN_BRANDS:
                current_brand = line
                current_model_group = None
                continue

            # Check if line is a brand listed with other brands (e.g., "Acura - Aleko - Alfa Romeo")
            if ' - ' in line and all(b.strip() in KNOWN_BRANDS for b in line.split(' - ') if b.strip()):
                continue

            if not current_brand:
                continue

            # Try to extract data from line
            # Format: [MODEL_GROUP] description - Mot.CODE-DISPcc-Powerkw/hp (date_from - date_to) FILTER_CODES

            # Check if this is a continuation of previous line
            if prev_line and not re.match(r'^[A-Z]', line) and not FRAM_PART_RE.search(line):
                prev_line = ""
                continue

            # Extract date range and parts
            date_match = re.search(r'\((\d{2}/\d{2,4}\s*-\s*(?:\d{2}/\d{2,4}\s*)?)\)', line)
            parts = extract_fram_parts(line)

            if parts:
                years = []
                if date_match:
                    years = parse_date_range(date_match.group(1))

                # Extract model name
                model_name = None
                # Check if line starts with an uppercase model group
                model_match = re.match(r'^([A-Z][A-Z0-9\s/\-]+?)\s+\S', line)
                if model_match:
                    potential_model = model_match.group(1).strip()
                    # If it looks like a model group (all caps, short)
                    if potential_model.isupper() and len(potential_model) < 30:
                        current_model_group = potential_model
                        model_name = current_model_group
                    else:
                        model_name = current_model_group or "Unknown"
                else:
                    model_name = current_model_group or "Unknown"

                if not years:
                    years = [2017]  # Default to catalog year

                for year in years:
                    for part in parts:
                        info = classify_filter(part)
                        if info:
                            entries.append({
                                'brand': current_brand,
                                'model': model_name,
                                'year': year,
                                'part_number': part,
                                'filter_type': info[0],
                            })

            prev_line = line

    return entries


def parse_cross_references(pdf):
    """Parse the equivalencias/cross-reference section."""
    xrefs = []

    for page_num in range(148, min(200, len(pdf.pages))):
        text = pdf.pages[page_num].extract_text()
        if not text:
            continue
        if 'EQUIVALENCIAS' not in text and 'Código' not in text:
            continue

        lines = text.split('\n')
        for line in lines:
            line = line.strip()
            if not line or 'EQUIVALENCIAS' in line or 'Código' in line:
                continue
            if re.match(r'^\d{1,3}$', line):
                continue
            # Skip brand header lines
            if re.match(r'^[A-Z][a-z]', line) and ' - ' in line:
                continue
            if line.istitle() or (line[0].isupper() and line[1:2].islower() and len(line.split()) <= 3):
                continue

            # Parse: CompetitorNumber FRAMNumber
            # FRAM numbers start with PH, CH, CA, PA, G, P, PS, CF, CFA
            match = re.match(r'^(\S+)\s+((?:PH|CH|CA|PA|PS|CF|CFA|G|P)\w+)', line)
            if match:
                competitor_pn = match.group(1).strip()
                fram_pn = match.group(2).strip()
                # Skip if competitor number looks like a FRAM number
                if re.match(r'^(PH|CH|CA|PA|PS|CF|CFA)', competitor_pn):
                    continue
                xrefs.append({
                    'competitor': competitor_pn,
                    'fram': fram_pn,
                })

    return xrefs


def main():
    print("=" * 70)
    print("IMPORTADOR - CATÁLOGO FRAM 2017")
    print("=" * 70)

    print(f"\n[1/6] Leyendo PDF: {PDF_PATH}")
    pdf = pypdf.PdfReader(PDF_PATH)
    print(f"      Total páginas: {len(pdf.pages)}")

    print("\n[2/6] Extrayendo datos del catálogo...")
    vehicle_entries = parse_vehicle_entries(pdf)
    cross_refs = parse_cross_references(pdf)
    print(f"      Entradas de vehículos: {len(vehicle_entries)}")
    print(f"      Equivalencias (cross-refs): {len(cross_refs)}")

    # Get unique parts
    unique_parts = {}
    for e in vehicle_entries:
        if e['part_number'] not in unique_parts:
            info = classify_filter(e['part_number'])
            if info:
                unique_parts[e['part_number']] = info
    print(f"      Partes únicas: {len(unique_parts)}")

    # Also get parts from cross-refs
    for xref in cross_refs:
        if xref['fram'] not in unique_parts:
            info = classify_filter(xref['fram'])
            if info:
                unique_parts[xref['fram']] = info

    print(f"      Partes únicas (incl. cross-refs): {len(unique_parts)}")

    conn = get_db()
    cursor = conn.cursor()

    # Create FRAM manufacturer
    print("\n[3/6] Creando fabricante FRAM...")
    # Check if Fram already exists (from Gonher import)
    fram_mfr_id = ensure_manufacturer(cursor, 'FRAM', 'aftermarket', 'standard', 'USA')
    print(f"      FRAM manufacturer_id: {fram_mfr_id}")

    # Create parts
    print("\n[4/6] Creando partes de filtros...")
    part_ids = {}
    parts_created = 0
    group_cache = {}

    for pn, (group_name, name_en, name_es) in unique_parts.items():
        if group_name not in group_cache:
            group_cache[group_name] = get_or_create_group(cursor, group_name)
        group_id = group_cache[group_name]
        if not group_id:
            continue

        full_name = f"{name_en} {pn}"
        full_name_es = f"{name_es} {pn}"
        part_id, created = get_or_create_part(
            cursor, pn, group_id, full_name, full_name_es, "FRAM Filter")
        part_ids[pn] = part_id
        if created:
            parts_created += 1

    print(f"      Partes creadas: {parts_created}")

    # Create vehicles and fitments
    print("\n[5/6] Creando vehículos y fitments...")
    vehicles_created = 0
    fitments_created = 0
    mye_cache = {}

    for entry in vehicle_entries:
        part_id = part_ids.get(entry['part_number'])
        if not part_id:
            continue

        cache_key = (entry['brand'], entry['model'], entry['year'])
        if cache_key not in mye_cache:
            brand_id = ensure_brand(cursor, entry['brand'])
            model_id = ensure_model(cursor, brand_id, entry['model'])
            year_id = ensure_year(cursor, entry['year'])

            cursor.execute(
                """SELECT mye.id FROM model_year_engine mye
                   JOIN models m ON mye.model_id = m.id
                   JOIN brands b ON m.brand_id = b.id
                   JOIN years y ON mye.year_id = y.id
                   WHERE UPPER(b.name) = UPPER(?) AND UPPER(m.name) = UPPER(?) AND y.year = ?
                   LIMIT 1""",
                (entry['brand'], entry['model'], entry['year']))
            existing = cursor.fetchone()

            if existing:
                mye_cache[cache_key] = existing['id']
            else:
                mye_id = ensure_mye(cursor, model_id, year_id)
                mye_cache[cache_key] = mye_id
                vehicles_created += 1

        mye_id = mye_cache[cache_key]

        cursor.execute(
            "SELECT id FROM vehicle_parts WHERE model_year_engine_id = ? AND part_id = ?",
            (mye_id, part_id))
        if not cursor.fetchone():
            cursor.execute(
                "INSERT INTO vehicle_parts (model_year_engine_id, part_id, quantity_required, fitment_notes) VALUES (?, ?, 1, ?)",
                (mye_id, part_id, f"Catálogo FRAM 2017 - {entry['filter_type']}"))
            fitments_created += 1

    print(f"      Vehículos creados: {vehicles_created}")
    print(f"      Fitments creados: {fitments_created}")

    # Create cross-references
    print("\n[6/6] Creando referencias cruzadas...")
    xrefs_created = 0

    # A) From equivalencias section
    for xref in cross_refs:
        fram_part_id = part_ids.get(xref['fram'])
        if not fram_part_id:
            continue

        cursor.execute(
            "SELECT id FROM part_cross_references WHERE part_id = ? AND cross_reference_number = ?",
            (fram_part_id, xref['competitor']))
        if not cursor.fetchone():
            cursor.execute(
                "INSERT INTO part_cross_references (part_id, cross_reference_number, reference_type, source) VALUES (?, ?, 'interchange', 'FRAM Equivalencias 2017')",
                (fram_part_id, xref['competitor']))
            xrefs_created += 1

    # B) Match FRAM parts to other brands' parts by vehicle fitment
    for pn, part_id in part_ids.items():
        cursor.execute("""
            SELECT DISTINCT p2.id, p2.oem_part_number
            FROM vehicle_parts vp1
            JOIN vehicle_parts vp2 ON vp1.model_year_engine_id = vp2.model_year_engine_id
            JOIN parts p2 ON vp2.part_id = p2.id
            WHERE vp1.part_id = ?
              AND p2.id != ?
              AND p2.group_id = (SELECT group_id FROM parts WHERE id = ?)
              AND p2.oem_part_number NOT LIKE 'PH%'
              AND p2.oem_part_number NOT LIKE 'CH%'
              AND p2.oem_part_number NOT LIKE 'CA%'
              AND p2.oem_part_number NOT LIKE 'PA%'
              AND p2.oem_part_number NOT LIKE 'CF%'
              AND p2.oem_part_number NOT LIKE 'CFA%'
            LIMIT 20
        """, (part_id, part_id, part_id))

        for row in cursor.fetchall():
            # Cross-ref FRAM → other
            cursor.execute(
                "SELECT id FROM part_cross_references WHERE part_id = ? AND cross_reference_number = ?",
                (part_id, row['oem_part_number']))
            if not cursor.fetchone():
                cursor.execute(
                    "INSERT INTO part_cross_references (part_id, cross_reference_number, reference_type, source) VALUES (?, ?, 'interchange', 'FRAM Catalog 2017')",
                    (part_id, row['oem_part_number']))
                xrefs_created += 1

            # Reverse cross-ref
            cursor.execute(
                "SELECT id FROM part_cross_references WHERE part_id = ? AND cross_reference_number = ?",
                (row['id'], pn))
            if not cursor.fetchone():
                cursor.execute(
                    "INSERT INTO part_cross_references (part_id, cross_reference_number, reference_type, source) VALUES (?, ?, 'interchange', 'FRAM Catalog 2017')",
                    (row['id'], pn))
                xrefs_created += 1

    print(f"      Cross-refs creadas: {xrefs_created}")

    conn.commit()
    conn.close()

    print("\n" + "=" * 70)
    print("IMPORTACIÓN FRAM COMPLETADA")
    print("=" * 70)
    print(f"""
RESUMEN:
  - Partes creadas:        {parts_created:,}
  - Vehículos creados:     {vehicles_created:,}
  - Fitments creados:      {fitments_created:,}
  - Cross-refs creadas:    {xrefs_created:,}
  - Equivalencias leídas:  {len(cross_refs):,}
""")


if __name__ == '__main__':
    main()
