#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Demo Procurement Plans Seeding Script for Procurement Service
Creates realistic procurement plans for demo template tenants using pre-defined UUIDs

This script runs as a Kubernetes init job inside the procurement-service container.
It populates the template tenants with comprehensive procurement plans.

Usage:
    python /app/scripts/demo/seed_demo_procurement_plans.py

Environment Variables Required:
    PROCUREMENT_DATABASE_URL - PostgreSQL connection string for procurement database
    DEMO_MODE - Set to 'production' for production seeding
    LOG_LEVEL - Logging level (default: INFO)

Note: No database lookups needed - all IDs are pre-defined in the JSON file
"""

import asyncio
import uuid
import sys
import os
import json
from datetime import datetime, timezone, timedelta, date
from pathlib import Path
import random
from decimal import Decimal

# Add app to path
sys.path.insert(0, str(Path(__file__).parent.parent.parent))

from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy import select, text
import structlog

from app.models.procurement_plan import ProcurementPlan, ProcurementRequirement

# Configure logging
structlog.configure(
    processors=[
        structlog.stdlib.add_log_level,
        structlog.processors.TimeStamper(fmt="iso"),
        structlog.dev.ConsoleRenderer()
    ]
)

logger = structlog.get_logger()

# Fixed Demo Tenant IDs (must match tenant service)
DEMO_TENANT_SAN_PABLO = uuid.UUID("a1b2c3d4-e5f6-47a8-b9c0-d1e2f3a4b5c6")  # Individual bakery
DEMO_TENANT_LA_ESPIGA = uuid.UUID("b2c3d4e5-f6a7-48b9-c0d1-e2f3a4b5c6d7")  # Central bakery

# Base reference date for date calculations
BASE_REFERENCE_DATE = datetime(2025, 1, 15, 12, 0, 0, tzinfo=timezone.utc)

# Hardcoded SKU to Ingredient ID mapping (no database lookups needed!)
INGREDIENT_ID_MAP = {
    "HAR-T55-001": "10000000-0000-0000-0000-000000000001",
    "HAR-T65-002": "10000000-0000-0000-0000-000000000002",
    "HAR-FUE-003": "10000000-0000-0000-0000-000000000003",
    "HAR-INT-004": "10000000-0000-0000-0000-000000000004",
    "HAR-CEN-005": "10000000-0000-0000-0000-000000000005",
    "HAR-ESP-006": "10000000-0000-0000-0000-000000000006",
    "LAC-MAN-001": "10000000-0000-0000-0000-000000000011",
    "LAC-LEC-002": "10000000-0000-0000-0000-000000000012",
    "LAC-NAT-003": "10000000-0000-0000-0000-000000000013",
    "LAC-HUE-004": "10000000-0000-0000-0000-000000000014",
    "LEV-FRE-001": "10000000-0000-0000-0000-000000000021",
    "LEV-SEC-002": "10000000-0000-0000-0000-000000000022",
    "BAS-SAL-001": "10000000-0000-0000-0000-000000000031",
    "BAS-AZU-002": "10000000-0000-0000-0000-000000000032",
    "ESP-CHO-001": "10000000-0000-0000-0000-000000000041",
    "ESP-ALM-002": "10000000-0000-0000-0000-000000000042",
    "ESP-VAI-004": "10000000-0000-0000-0000-000000000044",
    "ESP-CRE-005": "10000000-0000-0000-0000-000000000045",
}

# Ingredient costs (for requirement generation)
INGREDIENT_COSTS = {
    "HAR-T55-001": 0.85,
    "HAR-T65-002": 0.95,
    "HAR-FUE-003": 1.15,
    "HAR-INT-004": 1.20,
    "HAR-CEN-005": 1.30,
    "HAR-ESP-006": 2.45,
    "LAC-MAN-001": 6.50,
    "LAC-LEC-002": 0.95,
    "LAC-NAT-003": 3.20,
    "LAC-HUE-004": 0.25,
    "LEV-FRE-001": 4.80,
    "LEV-SEC-002": 12.50,
    "BAS-SAL-001": 0.60,
    "BAS-AZU-002": 0.90,
    "ESP-CHO-001": 15.50,
    "ESP-ALM-002": 8.90,
    "ESP-VAI-004": 3.50,
    "ESP-CRE-005": 7.20,
}


def calculate_date_from_offset(offset_days: int) -> date:
    """Calculate a date based on offset from BASE_REFERENCE_DATE"""
    return (BASE_REFERENCE_DATE + timedelta(days=offset_days)).date()


def calculate_datetime_from_offset(offset_days: int) -> datetime:
    """Calculate a datetime based on offset from BASE_REFERENCE_DATE"""
    return BASE_REFERENCE_DATE + timedelta(days=offset_days)


def weighted_choice(choices: list) -> dict:
    """Make a weighted random choice from list of dicts with 'weight' key"""
    total_weight = sum(c.get("weight", 1.0) for c in choices)
    r = random.uniform(0, total_weight)

    cumulative = 0
    for choice in choices:
        cumulative += choice.get("weight", 1.0)
        if r <= cumulative:
            return choice

    return choices[-1]


def generate_plan_number(tenant_id: uuid.UUID, index: int, plan_type: str) -> str:
    """Generate a unique plan number"""
    tenant_prefix = "SP" if tenant_id == DEMO_TENANT_SAN_PABLO else "LE"
    type_code = plan_type[0:3].upper()
    return f"PROC-{tenant_prefix}-{type_code}-{BASE_REFERENCE_DATE.year}-{index:03d}"


async def generate_procurement_for_tenant(
    db: AsyncSession,
    tenant_id: uuid.UUID,
    tenant_name: str,
    business_model: str,
    config: dict
) -> dict:
    """Generate procurement plans and requirements for a specific tenant"""
    logger.info("─" * 80)
    logger.info(f"Generating procurement data for: {tenant_name}")
    logger.info(f"Tenant ID: {tenant_id}")
    logger.info("─" * 80)

    # Check if procurement plans already exist
    result = await db.execute(
        select(ProcurementPlan).where(ProcurementPlan.tenant_id == tenant_id).limit(1)
    )
    existing = result.scalar_one_or_none()

    if existing:
        logger.info(f"   ⏭️  Procurement plans already exist for {tenant_name}, skipping seed")
        return {
            "tenant_id": str(tenant_id),
            "plans_created": 0,
            "requirements_created": 0,
            "skipped": True
        }

    proc_config = config["procurement_config"]
    total_plans = proc_config["plans_per_tenant"]

    plans_created = 0
    requirements_created = 0

    for i in range(total_plans):
        # Determine temporal distribution
        rand_temporal = random.random()
        cumulative = 0
        temporal_category = None

        for category, details in proc_config["temporal_distribution"].items():
            cumulative += details["percentage"]
            if rand_temporal <= cumulative:
                temporal_category = details
                break

        if not temporal_category:
            temporal_category = proc_config["temporal_distribution"]["completed"]

        # Calculate plan date
        offset_days = random.randint(
            temporal_category["offset_days_min"],
            temporal_category["offset_days_max"]
        )
        plan_date = calculate_date_from_offset(offset_days)

        # Select status
        status = random.choice(temporal_category["statuses"])

        # Select plan type
        plan_type_choice = weighted_choice(proc_config["plan_types"])
        plan_type = plan_type_choice["type"]

        # Select priority
        priority_rand = random.random()
        cumulative_priority = 0
        priority = "normal"
        for p, weight in proc_config["priorities"].items():
            cumulative_priority += weight
            if priority_rand <= cumulative_priority:
                priority = p
                break

        # Select procurement strategy
        strategy_choice = weighted_choice(proc_config["procurement_strategies"])
        procurement_strategy = strategy_choice["strategy"]

        # Select supply risk level
        risk_rand = random.random()
        cumulative_risk = 0
        supply_risk_level = "low"
        for risk, weight in proc_config["risk_levels"].items():
            cumulative_risk += weight
            if risk_rand <= cumulative_risk:
                supply_risk_level = risk
                break

        # Calculate planning horizon
        planning_horizon = proc_config["planning_horizon_days"][business_model]

        # Calculate period dates
        period_start = plan_date
        period_end = plan_date + timedelta(days=planning_horizon)

        # Generate plan number
        plan_number = generate_plan_number(tenant_id, i + 1, plan_type)

        # Calculate safety stock buffer
        safety_stock_buffer = Decimal(str(random.uniform(
            proc_config["safety_stock_percentage"]["min"],
            proc_config["safety_stock_percentage"]["max"]
        )))

        # Calculate approval/execution dates based on status
        approved_at = None
        execution_started_at = None
        execution_completed_at = None
        approved_by = None

        if status in ["approved", "in_execution", "completed"]:
            approved_at = calculate_datetime_from_offset(offset_days - 1)
            approved_by = uuid.uuid4()  # Would be actual user ID

        if status in ["in_execution", "completed"]:
            execution_started_at = calculate_datetime_from_offset(offset_days)

        if status == "completed":
            execution_completed_at = calculate_datetime_from_offset(offset_days + planning_horizon)

        # Calculate performance metrics for completed plans
        fulfillment_rate = None
        on_time_delivery_rate = None
        cost_accuracy = None
        quality_score = None

        if status == "completed":
            metrics = proc_config["performance_metrics"]
            fulfillment_rate = Decimal(str(random.uniform(
                metrics["fulfillment_rate"]["min"],
                metrics["fulfillment_rate"]["max"]
            )))
            on_time_delivery_rate = Decimal(str(random.uniform(
                metrics["on_time_delivery"]["min"],
                metrics["on_time_delivery"]["max"]
            )))
            cost_accuracy = Decimal(str(random.uniform(
                metrics["cost_accuracy"]["min"],
                metrics["cost_accuracy"]["max"]
            )))
            quality_score = Decimal(str(random.uniform(
                metrics["quality_score"]["min"],
                metrics["quality_score"]["max"]
            )))

        # Create procurement plan
        plan = ProcurementPlan(
            id=uuid.uuid4(),
            tenant_id=tenant_id,
            plan_number=plan_number,
            plan_date=plan_date,
            plan_period_start=period_start,
            plan_period_end=period_end,
            planning_horizon_days=planning_horizon,
            status=status,
            plan_type=plan_type,
            priority=priority,
            business_model=business_model,
            procurement_strategy=procurement_strategy,
            total_requirements=0,  # Will update after adding requirements
            total_estimated_cost=Decimal("0.00"),  # Will calculate
            total_approved_cost=Decimal("0.00"),
            safety_stock_buffer=safety_stock_buffer,
            supply_risk_level=supply_risk_level,
            demand_forecast_confidence=Decimal(str(random.uniform(7.0, 9.5))),
            approved_at=approved_at,
            approved_by=approved_by,
            execution_started_at=execution_started_at,
            execution_completed_at=execution_completed_at,
            fulfillment_rate=fulfillment_rate,
            on_time_delivery_rate=on_time_delivery_rate,
            cost_accuracy=cost_accuracy,
            quality_score=quality_score,
            created_at=calculate_datetime_from_offset(offset_days - 2),
            updated_at=calculate_datetime_from_offset(offset_days)
        )

        db.add(plan)
        await db.flush()  # Get plan ID

        # Generate requirements for this plan
        num_requirements = random.randint(
            proc_config["requirements_per_plan"]["min"],
            proc_config["requirements_per_plan"]["max"]
        )

        # Select random ingredients
        selected_ingredients = random.sample(
            list(INGREDIENT_ID_MAP.keys()),
            min(num_requirements, len(INGREDIENT_ID_MAP))
        )

        total_estimated_cost = Decimal("0.00")

        for req_num, ingredient_sku in enumerate(selected_ingredients, 1):
            # Get ingredient ID from hardcoded mapping
            ingredient_id_str = INGREDIENT_ID_MAP.get(ingredient_sku)
            if not ingredient_id_str:
                logger.warning(f"      ⚠️  Ingredient SKU not in mapping: {ingredient_sku}")
                continue

            # Generate tenant-specific ingredient ID
            base_ingredient_id = uuid.UUID(ingredient_id_str)
            tenant_int = int(tenant_id.hex, 16)
            ingredient_id = uuid.UUID(int=tenant_int ^ int(base_ingredient_id.hex, 16))

            # Get quantity range for category
            category = ingredient_sku.split("-")[0]  # HAR, LAC, LEV, BAS, ESP
            cantidad_range = proc_config["quantity_ranges"].get(
                category,
                {"min": 50.0, "max": 200.0}
            )

            # Calculate required quantity
            required_quantity = Decimal(str(random.uniform(
                cantidad_range["min"],
                cantidad_range["max"]
            )))

            # Calculate safety stock
            safety_stock_quantity = required_quantity * (safety_stock_buffer / 100)

            # Total quantity needed
            total_quantity_needed = required_quantity + safety_stock_quantity

            # Current stock simulation
            current_stock_level = required_quantity * Decimal(str(random.uniform(0.1, 0.4)))
            reserved_stock = current_stock_level * Decimal(str(random.uniform(0.0, 0.3)))
            available_stock = current_stock_level - reserved_stock

            # Net requirement
            net_requirement = total_quantity_needed - available_stock

            # Demand breakdown
            order_demand = required_quantity * Decimal(str(random.uniform(0.5, 0.7)))
            production_demand = required_quantity * Decimal(str(random.uniform(0.2, 0.4)))
            forecast_demand = required_quantity * Decimal(str(random.uniform(0.05, 0.15)))
            buffer_demand = safety_stock_quantity

            # Pricing
            estimated_unit_cost = Decimal(str(INGREDIENT_COSTS.get(ingredient_sku, 1.0))) * Decimal(str(random.uniform(0.95, 1.05)))
            estimated_total_cost = estimated_unit_cost * net_requirement

            # Timing
            lead_time_days = random.randint(1, 5)
            required_by_date = period_start + timedelta(days=random.randint(3, planning_horizon - 2))
            lead_time_buffer_days = random.randint(1, 2)
            suggested_order_date = required_by_date - timedelta(days=lead_time_days + lead_time_buffer_days)
            latest_order_date = required_by_date - timedelta(days=lead_time_days)

            # Requirement status based on plan status
            if status == "draft":
                req_status = "pending"
            elif status == "pending_approval":
                req_status = "pending"
            elif status == "approved":
                req_status = "approved"
            elif status == "in_execution":
                req_status = random.choice(["ordered", "partially_received"])
            elif status == "completed":
                req_status = "received"
            else:
                req_status = "pending"

            # Requirement priority
            if priority == "critical":
                req_priority = "critical"
            elif priority == "high":
                req_priority = random.choice(["high", "critical"])
            else:
                req_priority = random.choice(["normal", "high"])

            # Risk level
            if supply_risk_level == "critical":
                req_risk_level = random.choice(["high", "critical"])
            elif supply_risk_level == "high":
                req_risk_level = random.choice(["medium", "high"])
            else:
                req_risk_level = "low"

            # Create requirement
            requirement = ProcurementRequirement(
                id=uuid.uuid4(),
                plan_id=plan.id,
                requirement_number=f"{plan_number}-REQ-{req_num:03d}",
                product_id=ingredient_id,
                product_name=f"Ingrediente {ingredient_sku}",
                product_sku=ingredient_sku,
                product_category=category,
                product_type="ingredient",
                required_quantity=required_quantity,
                unit_of_measure="kg",
                safety_stock_quantity=safety_stock_quantity,
                total_quantity_needed=total_quantity_needed,
                current_stock_level=current_stock_level,
                reserved_stock=reserved_stock,
                available_stock=available_stock,
                net_requirement=net_requirement,
                order_demand=order_demand,
                production_demand=production_demand,
                forecast_demand=forecast_demand,
                buffer_demand=buffer_demand,
                supplier_lead_time_days=lead_time_days,
                minimum_order_quantity=Decimal(str(random.choice([1, 5, 10, 25]))),
                estimated_unit_cost=estimated_unit_cost,
                estimated_total_cost=estimated_total_cost,
                required_by_date=required_by_date,
                lead_time_buffer_days=lead_time_buffer_days,
                suggested_order_date=suggested_order_date,
                latest_order_date=latest_order_date,
                shelf_life_days=random.choice([30, 60, 90, 180, 365]),
                status=req_status,
                priority=req_priority,
                risk_level=req_risk_level,
                created_at=plan.created_at,
                updated_at=plan.updated_at
            )

            db.add(requirement)
            total_estimated_cost += estimated_total_cost
            requirements_created += 1

        # Update plan totals
        plan.total_requirements = num_requirements
        plan.total_estimated_cost = total_estimated_cost
        if status in ["approved", "in_execution", "completed"]:
            plan.total_approved_cost = total_estimated_cost * Decimal(str(random.uniform(0.95, 1.05)))

        plans_created += 1

    await db.commit()
    logger.info(f"   📊 Successfully created {plans_created} plans with {requirements_created} requirements for {tenant_name}")
    logger.info("")

    return {
        "tenant_id": str(tenant_id),
        "plans_created": plans_created,
        "requirements_created": requirements_created,
        "skipped": False
    }


async def seed_all(db: AsyncSession):
    """Seed all demo tenants with procurement data"""
    logger.info("=" * 80)
    logger.info("🚚 Starting Demo Procurement Plans Seeding")
    logger.info("=" * 80)

    # Load configuration
    config = {
        "procurement_config": {
            "plans_per_tenant": 8,
            "requirements_per_plan": {"min": 3, "max": 8},
            "planning_horizon_days": {
                "individual_bakery": 30,
                "central_bakery": 45
            },
            "safety_stock_percentage": {"min": 15.0, "max": 25.0},
            "temporal_distribution": {
                "completed": {
                    "percentage": 0.3,
                    "offset_days_min": -15,
                    "offset_days_max": -1,
                    "statuses": ["completed"]
                },
                "in_execution": {
                    "percentage": 0.2,
                    "offset_days_min": -5,
                    "offset_days_max": 2,
                    "statuses": ["in_execution", "partially_received"]
                },
                "approved": {
                    "percentage": 0.2,
                    "offset_days_min": -2,
                    "offset_days_max": 1,
                    "statuses": ["approved"]
                },
                "pending_approval": {
                    "percentage": 0.15,
                    "offset_days_min": 0,
                    "offset_days_max": 3,
                    "statuses": ["pending_approval"]
                },
                "draft": {
                    "percentage": 0.15,
                    "offset_days_min": 0,
                    "offset_days_max": 5,
                    "statuses": ["draft"]
                }
            },
            "plan_types": [
                {"type": "regular", "weight": 0.7},
                {"type": "seasonal", "weight": 0.2},
                {"type": "emergency", "weight": 0.1}
            ],
            "priorities": {
                "normal": 0.7,
                "high": 0.25,
                "critical": 0.05
            },
            "procurement_strategies": [
                {"strategy": "just_in_time", "weight": 0.6},
                {"strategy": "bulk", "weight": 0.3},
                {"strategy": "mixed", "weight": 0.1}
            ],
            "risk_levels": {
                "low": 0.6,
                "medium": 0.3,
                "high": 0.08,
                "critical": 0.02
            },
            "quantity_ranges": {
                "HAR": {"min": 50.0, "max": 500.0},  # Harinas
                "LAC": {"min": 20.0, "max": 200.0},  # Lácteos
                "LEV": {"min": 5.0, "max": 50.0},    # Levaduras
                "BAS": {"min": 10.0, "max": 100.0},  # Básicos
                "ESP": {"min": 1.0, "max": 20.0}     # Especiales
            },
            "performance_metrics": {
                "fulfillment_rate": {"min": 85.0, "max": 98.0},
                "on_time_delivery": {"min": 80.0, "max": 95.0},
                "cost_accuracy": {"min": 90.0, "max": 99.0},
                "quality_score": {"min": 7.0, "max": 9.5}
            }
        }
    }

    results = []

    # Seed San Pablo (Individual Bakery)
    result_san_pablo = await generate_procurement_for_tenant(
        db,
        DEMO_TENANT_SAN_PABLO,
        "Panadería San Pablo (Individual Bakery)",
        "individual_bakery",
        config
    )
    results.append(result_san_pablo)

    # Seed La Espiga (Central Bakery)
    result_la_espiga = await generate_procurement_for_tenant(
        db,
        DEMO_TENANT_LA_ESPIGA,
        "Panadería La Espiga (Central Bakery)",
        "central_bakery",
        config
    )
    results.append(result_la_espiga)

    total_plans = sum(r["plans_created"] for r in results)
    total_requirements = sum(r["requirements_created"] for r in results)

    logger.info("=" * 80)
    logger.info("✅ Demo Procurement Plans Seeding Completed")
    logger.info("=" * 80)

    return {
        "results": results,
        "total_plans_created": total_plans,
        "total_requirements_created": total_requirements,
        "status": "completed"
    }


async def main():
    """Main execution function"""
    logger.info("Demo Procurement Plans Seeding Script Starting")
    logger.info("Mode: %s", os.getenv("DEMO_MODE", "development"))
    logger.info("Log Level: %s", os.getenv("LOG_LEVEL", "INFO"))

    # Get database URL from environment
    database_url = os.getenv("PROCUREMENT_DATABASE_URL") or os.getenv("DATABASE_URL")
    if not database_url:
        logger.error("❌ PROCUREMENT_DATABASE_URL or DATABASE_URL environment variable must be set")
        return 1

    # Ensure asyncpg driver
    if database_url.startswith("postgresql://"):
        database_url = database_url.replace("postgresql://", "postgresql+asyncpg://", 1)

    logger.info("Connecting to procurement database")

    # Create async engine
    engine = create_async_engine(
        database_url,
        echo=False,
        pool_pre_ping=True,
        pool_size=5,
        max_overflow=10
    )

    async_session = sessionmaker(
        engine,
        class_=AsyncSession,
        expire_on_commit=False
    )

    try:
        async with async_session() as session:
            result = await seed_all(session)

            logger.info("")
            logger.info("📊 Seeding Summary:")
            logger.info(f"   ✅ Total Plans: {result['total_plans_created']}")
            logger.info(f"   ✅ Total Requirements: {result['total_requirements_created']}")
            logger.info(f"   ✅ Status: {result['status']}")
            logger.info("")

            # Print per-tenant details
            for tenant_result in result["results"]:
                tenant_id = tenant_result["tenant_id"]
                plans = tenant_result["plans_created"]
                requirements = tenant_result["requirements_created"]
                skipped = tenant_result.get("skipped", False)
                status = "SKIPPED (already exists)" if skipped else f"CREATED {plans} plans, {requirements} requirements"
                logger.info(f"   Tenant {tenant_id}: {status}")

            logger.info("")
            logger.info("🎉 Success! Procurement plans are ready for demo sessions.")
            logger.info("")
            logger.info("Plans created:")
            logger.info("  • 8 Regular procurement plans per tenant")
            logger.info("  • 3-8 Requirements per plan")
            logger.info("  • Various statuses: draft, pending, approved, in execution, completed")
            logger.info("  • Different priorities and risk levels")
            logger.info("")
            logger.info("Note: All IDs are pre-defined and hardcoded for cross-service consistency")
            logger.info("")

            return 0

    except Exception as e:
        logger.error("=" * 80)
        logger.error("❌ Demo Procurement Plans Seeding Failed")
        logger.error("=" * 80)
        logger.error("Error: %s", str(e))
        logger.error("", exc_info=True)
        return 1
    finally:
        await engine.dispose()


if __name__ == "__main__":
    exit_code = asyncio.run(main())
    sys.exit(exit_code)
