"""
Migration script to add new invoice features:
- document_type, order_number columns on invoices
- duplicate_status, duplicate_of_id, related_document_id columns on invoices
- line_items table

Run this script once after deploying the new code.
"""
import asyncio
import logging
from sqlalchemy import text
from database import engine

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)


async def run_migration():
    """Add new columns and tables for invoice features."""
    # Always run all migrations - each has its own try/except to handle existing columns
    logger.info("Running database migrations...")

    # Run each migration in its own transaction
    migrations = [
        "ALTER TABLE invoices ADD COLUMN document_type VARCHAR(50) DEFAULT 'invoice'",
        "ALTER TABLE invoices ADD COLUMN order_number VARCHAR(100)",
        "ALTER TABLE invoices ADD COLUMN duplicate_status VARCHAR(50)",
        "ALTER TABLE invoices ADD COLUMN duplicate_of_id INTEGER REFERENCES invoices(id)",
        "ALTER TABLE invoices ADD COLUMN related_document_id INTEGER REFERENCES invoices(id)",
    ]

    for sql in migrations:
        try:
            async with engine.begin() as conn:
                await conn.execute(text(sql))
                logger.info(f"Executed: {sql[:60]}...")
        except Exception as e:
            if "already exists" in str(e).lower() or "duplicate column" in str(e).lower():
                logger.info(f"Column already exists, skipping")
            else:
                logger.warning(f"Migration warning: {e}")

    # Create line_items table
    create_line_items = """
    CREATE TABLE IF NOT EXISTS line_items (
        id SERIAL PRIMARY KEY,
        invoice_id INTEGER NOT NULL REFERENCES invoices(id) ON DELETE CASCADE,
        description TEXT,
        quantity NUMERIC(10, 3),
        unit_price NUMERIC(10, 2),
        amount NUMERIC(10, 2),
        product_code VARCHAR(100),
        line_number INTEGER DEFAULT 0,
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    )
    """
    try:
        async with engine.begin() as conn:
            await conn.execute(text(create_line_items))
            logger.info("Created line_items table")
    except Exception as e:
        logger.warning(f"line_items table: {e}")

    # Create index
    try:
        async with engine.begin() as conn:
            await conn.execute(text(
                "CREATE INDEX IF NOT EXISTS ix_line_items_invoice_id ON line_items(invoice_id)"
            ))
            logger.info("Created index on line_items.invoice_id")
    except Exception as e:
        logger.warning(f"Index: {e}")

    # Add aliases column to suppliers table
    try:
        async with engine.begin() as conn:
            await conn.execute(text(
                "ALTER TABLE suppliers ADD COLUMN aliases JSON DEFAULT '[]'"
            ))
            logger.info("Added aliases column to suppliers")
    except Exception as e:
        if "already exists" in str(e).lower() or "duplicate column" in str(e).lower():
            logger.info("aliases column already exists")
        else:
            logger.warning(f"aliases column: {e}")

    # Add net_total column to invoices table
    try:
        async with engine.begin() as conn:
            await conn.execute(text(
                "ALTER TABLE invoices ADD COLUMN net_total NUMERIC(10, 2)"
            ))
            logger.info("Added net_total column to invoices")
    except Exception as e:
        if "already exists" in str(e).lower() or "duplicate column" in str(e).lower():
            logger.info("net_total column already exists")
        else:
            logger.warning(f"net_total column: {e}")

    # Add is_non_stock column to line_items table
    try:
        async with engine.begin() as conn:
            await conn.execute(text(
                "ALTER TABLE line_items ADD COLUMN is_non_stock BOOLEAN DEFAULT FALSE"
            ))
            logger.info("Added is_non_stock column to line_items")
    except Exception as e:
        if "already exists" in str(e).lower() or "duplicate column" in str(e).lower():
            logger.info("is_non_stock column already exists")
        else:
            logger.warning(f"is_non_stock column: {e}")

    # Add vendor_name column to invoices table
    try:
        async with engine.begin() as conn:
            await conn.execute(text(
                "ALTER TABLE invoices ADD COLUMN vendor_name VARCHAR(255)"
            ))
            logger.info("Added vendor_name column to invoices")
    except Exception as e:
        if "already exists" in str(e).lower() or "duplicate column" in str(e).lower():
            logger.info("vendor_name column already exists")
        else:
            logger.warning(f"vendor_name column: {e}")

    # Add ocr_raw_json column to invoices table (for storing full Azure response)
    try:
        async with engine.begin() as conn:
            await conn.execute(text(
                "ALTER TABLE invoices ADD COLUMN ocr_raw_json TEXT"
            ))
            logger.info("Added ocr_raw_json column to invoices")
    except Exception as e:
        if "already exists" in str(e).lower() or "duplicate column" in str(e).lower():
            logger.info("ocr_raw_json column already exists")
        else:
            logger.warning(f"ocr_raw_json column: {e}")

    # Add supplier_match_type column to invoices table (for fuzzy matching)
    try:
        async with engine.begin() as conn:
            await conn.execute(text(
                "ALTER TABLE invoices ADD COLUMN supplier_match_type VARCHAR(20)"
            ))
            logger.info("Added supplier_match_type column to invoices")
    except Exception as e:
        if "already exists" in str(e).lower() or "duplicate column" in str(e).lower():
            logger.info("supplier_match_type column already exists")
        else:
            logger.warning(f"supplier_match_type column: {e}")

    # Create field_mappings table
    create_field_mappings = """
    CREATE TABLE IF NOT EXISTS field_mappings (
        id SERIAL PRIMARY KEY,
        kitchen_id INTEGER NOT NULL REFERENCES kitchens(id),
        supplier_id INTEGER REFERENCES suppliers(id),
        source_field VARCHAR(100) NOT NULL,
        target_field VARCHAR(100) NOT NULL,
        field_type VARCHAR(20) DEFAULT 'invoice',
        transform VARCHAR(50) DEFAULT 'direct',
        priority INTEGER DEFAULT 0,
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    )
    """
    try:
        async with engine.begin() as conn:
            await conn.execute(text(create_field_mappings))
            logger.info("Created field_mappings table")
    except Exception as e:
        logger.warning(f"field_mappings table: {e}")

    # Create index on field_mappings
    try:
        async with engine.begin() as conn:
            await conn.execute(text(
                "CREATE INDEX IF NOT EXISTS ix_field_mappings_kitchen_id ON field_mappings(kitchen_id)"
            ))
            await conn.execute(text(
                "CREATE INDEX IF NOT EXISTS ix_field_mappings_supplier_id ON field_mappings(supplier_id)"
            ))
            logger.info("Created indexes on field_mappings")
    except Exception as e:
        logger.warning(f"field_mappings indexes: {e}")

    # Add unit column to line_items table
    try:
        async with engine.begin() as conn:
            await conn.execute(text(
                "ALTER TABLE line_items ADD COLUMN unit VARCHAR(50)"
            ))
            logger.info("Added unit column to line_items")
    except Exception as e:
        if "already exists" in str(e).lower() or "duplicate column" in str(e).lower():
            logger.info("unit column already exists")
        else:
            logger.warning(f"unit column: {e}")

    # Add order_quantity column to line_items table
    try:
        async with engine.begin() as conn:
            await conn.execute(text(
                "ALTER TABLE line_items ADD COLUMN order_quantity NUMERIC(10, 3)"
            ))
            logger.info("Added order_quantity column to line_items")
    except Exception as e:
        if "already exists" in str(e).lower() or "duplicate column" in str(e).lower():
            logger.info("order_quantity column already exists")
        else:
            logger.warning(f"order_quantity column: {e}")

    # Add tax_rate column to line_items table
    try:
        async with engine.begin() as conn:
            await conn.execute(text(
                "ALTER TABLE line_items ADD COLUMN tax_rate VARCHAR(50)"
            ))
            logger.info("Added tax_rate column to line_items")
    except Exception as e:
        if "already exists" in str(e).lower() or "duplicate column" in str(e).lower():
            logger.info("tax_rate column already exists")
        else:
            logger.warning(f"tax_rate column: {e}")

    # Add tax_amount column to line_items table
    try:
        async with engine.begin() as conn:
            await conn.execute(text(
                "ALTER TABLE line_items ADD COLUMN tax_amount NUMERIC(10, 2)"
            ))
            logger.info("Added tax_amount column to line_items")
    except Exception as e:
        if "already exists" in str(e).lower() or "duplicate column" in str(e).lower():
            logger.info("tax_amount column already exists")
        else:
            logger.warning(f"tax_amount column: {e}")

    # Add raw_content column to line_items table (for pack size parsing)
    try:
        async with engine.begin() as conn:
            await conn.execute(text(
                "ALTER TABLE line_items ADD COLUMN raw_content TEXT"
            ))
            logger.info("Added raw_content column to line_items")
    except Exception as e:
        if "already exists" in str(e).lower() or "duplicate column" in str(e).lower():
            logger.info("raw_content column already exists")
        else:
            logger.warning(f"raw_content column: {e}")

    # Add pack_quantity column to line_items table
    try:
        async with engine.begin() as conn:
            await conn.execute(text(
                "ALTER TABLE line_items ADD COLUMN pack_quantity INTEGER"
            ))
            logger.info("Added pack_quantity column to line_items")
    except Exception as e:
        if "already exists" in str(e).lower() or "duplicate column" in str(e).lower():
            logger.info("pack_quantity column already exists")
        else:
            logger.warning(f"pack_quantity column: {e}")

    # Add unit_size column to line_items table
    try:
        async with engine.begin() as conn:
            await conn.execute(text(
                "ALTER TABLE line_items ADD COLUMN unit_size NUMERIC(10, 3)"
            ))
            logger.info("Added unit_size column to line_items")
    except Exception as e:
        if "already exists" in str(e).lower() or "duplicate column" in str(e).lower():
            logger.info("unit_size column already exists")
        else:
            logger.warning(f"unit_size column: {e}")

    # Add unit_size_type column to line_items table
    try:
        async with engine.begin() as conn:
            await conn.execute(text(
                "ALTER TABLE line_items ADD COLUMN unit_size_type VARCHAR(10)"
            ))
            logger.info("Added unit_size_type column to line_items")
    except Exception as e:
        if "already exists" in str(e).lower() or "duplicate column" in str(e).lower():
            logger.info("unit_size_type column already exists")
        else:
            logger.warning(f"unit_size_type column: {e}")

    # Add portions_per_unit column to line_items table
    try:
        async with engine.begin() as conn:
            await conn.execute(text(
                "ALTER TABLE line_items ADD COLUMN portions_per_unit INTEGER DEFAULT 1"
            ))
            logger.info("Added portions_per_unit column to line_items")
    except Exception as e:
        if "already exists" in str(e).lower() or "duplicate column" in str(e).lower():
            logger.info("portions_per_unit column already exists")
        else:
            logger.warning(f"portions_per_unit column: {e}")

    # Add cost_per_item column to line_items table
    try:
        async with engine.begin() as conn:
            await conn.execute(text(
                "ALTER TABLE line_items ADD COLUMN cost_per_item NUMERIC(10, 4)"
            ))
            logger.info("Added cost_per_item column to line_items")
    except Exception as e:
        if "already exists" in str(e).lower() or "duplicate column" in str(e).lower():
            logger.info("cost_per_item column already exists")
        else:
            logger.warning(f"cost_per_item column: {e}")

    # Add cost_per_portion column to line_items table
    try:
        async with engine.begin() as conn:
            await conn.execute(text(
                "ALTER TABLE line_items ADD COLUMN cost_per_portion NUMERIC(10, 4)"
            ))
            logger.info("Added cost_per_portion column to line_items")
    except Exception as e:
        if "already exists" in str(e).lower() or "duplicate column" in str(e).lower():
            logger.info("cost_per_portion column already exists")
        else:
            logger.warning(f"cost_per_portion column: {e}")

    # Create product_definitions table for persistent portion/pack data
    create_product_definitions = """
    CREATE TABLE IF NOT EXISTS product_definitions (
        id SERIAL PRIMARY KEY,
        kitchen_id INTEGER NOT NULL REFERENCES kitchens(id),
        supplier_id INTEGER REFERENCES suppliers(id),
        product_code VARCHAR(100),
        description_pattern VARCHAR(255),
        pack_quantity INTEGER,
        unit_size NUMERIC(10, 3),
        unit_size_type VARCHAR(10),
        portions_per_unit INTEGER,
        portion_description VARCHAR(100),
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        UNIQUE(kitchen_id, supplier_id, product_code)
    )
    """
    try:
        async with engine.begin() as conn:
            await conn.execute(text(create_product_definitions))
            logger.info("Created product_definitions table")
    except Exception as e:
        logger.warning(f"product_definitions table: {e}")

    # Create indexes on product_definitions
    try:
        async with engine.begin() as conn:
            await conn.execute(text(
                "CREATE INDEX IF NOT EXISTS ix_product_definitions_kitchen_id ON product_definitions(kitchen_id)"
            ))
            await conn.execute(text(
                "CREATE INDEX IF NOT EXISTS ix_product_definitions_supplier_id ON product_definitions(supplier_id)"
            ))
            await conn.execute(text(
                "CREATE INDEX IF NOT EXISTS ix_product_definitions_product_code ON product_definitions(product_code)"
            ))
            logger.info("Created indexes on product_definitions")
    except Exception as e:
        logger.warning(f"product_definitions indexes: {e}")

    # Remove default from portions_per_unit column (make it truly nullable)
    # PostgreSQL doesn't have a simple ALTER COLUMN DROP DEFAULT, but we can change the default to NULL
    try:
        async with engine.begin() as conn:
            await conn.execute(text(
                "ALTER TABLE line_items ALTER COLUMN portions_per_unit DROP DEFAULT"
            ))
            logger.info("Removed default from portions_per_unit column")
    except Exception as e:
        logger.warning(f"portions_per_unit default removal: {e}")

    # Add saved_by_user_id column to product_definitions table
    try:
        async with engine.begin() as conn:
            await conn.execute(text(
                "ALTER TABLE product_definitions ADD COLUMN saved_by_user_id INTEGER REFERENCES users(id)"
            ))
            logger.info("Added saved_by_user_id column to product_definitions")
    except Exception as e:
        if "already exists" in str(e).lower() or "duplicate column" in str(e).lower():
            logger.info("saved_by_user_id column already exists")
        else:
            logger.warning(f"saved_by_user_id column: {e}")

    # Add source_invoice_id column to product_definitions table
    try:
        async with engine.begin() as conn:
            await conn.execute(text(
                "ALTER TABLE product_definitions ADD COLUMN source_invoice_id INTEGER REFERENCES invoices(id) ON DELETE SET NULL"
            ))
            logger.info("Added source_invoice_id column to product_definitions")
    except Exception as e:
        if "already exists" in str(e).lower() or "duplicate column" in str(e).lower():
            logger.info("source_invoice_id column already exists")
        else:
            logger.warning(f"source_invoice_id column: {e}")

    # Add source_invoice_number column to product_definitions table
    try:
        async with engine.begin() as conn:
            await conn.execute(text(
                "ALTER TABLE product_definitions ADD COLUMN source_invoice_number VARCHAR(100)"
            ))
            logger.info("Added source_invoice_number column to product_definitions")
    except Exception as e:
        if "already exists" in str(e).lower() or "duplicate column" in str(e).lower():
            logger.info("source_invoice_number column already exists")
        else:
            logger.warning(f"source_invoice_number column: {e}")

    logger.info("Migration completed successfully!")


if __name__ == "__main__":
    asyncio.run(run_migration())
