"""
Internal Demo Cloning API for Procurement Service
Service-to-service endpoint for cloning procurement and purchase order data
"""

from fastapi import APIRouter, Depends, HTTPException, Header
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, delete, func
import structlog
import uuid
from datetime import datetime, timezone, timedelta, date
from typing import Optional
import os

from app.core.database import get_db
from app.models.procurement_plan import ProcurementPlan, ProcurementRequirement
from app.models.purchase_order import PurchaseOrder, PurchaseOrderItem
from app.models.replenishment import ReplenishmentPlan, ReplenishmentPlanItem
from shared.utils.demo_dates import adjust_date_for_demo, BASE_REFERENCE_DATE

logger = structlog.get_logger()
router = APIRouter(prefix="/internal/demo", tags=["internal"])

# Internal API key for service-to-service auth
INTERNAL_API_KEY = os.getenv("INTERNAL_API_KEY", "dev-internal-key-change-in-production")

# Base demo tenant IDs
DEMO_TENANT_SAN_PABLO = "a1b2c3d4-e5f6-47a8-b9c0-d1e2f3a4b5c6"
DEMO_TENANT_LA_ESPIGA = "b2c3d4e5-f6a7-48b9-c0d1-e2f3a4b5c6d7"


def verify_internal_api_key(x_internal_api_key: Optional[str] = Header(None)):
    """Verify internal API key for service-to-service communication"""
    if x_internal_api_key != INTERNAL_API_KEY:
        logger.warning("Unauthorized internal API access attempted")
        raise HTTPException(status_code=403, detail="Invalid internal API key")
    return True


@router.post("/clone")
async def clone_demo_data(
    base_tenant_id: str,
    virtual_tenant_id: str,
    demo_account_type: str,
    session_id: Optional[str] = None,
    session_created_at: Optional[str] = None,
    db: AsyncSession = Depends(get_db),
    _: bool = Depends(verify_internal_api_key)
):
    """
    Clone procurement service data for a virtual demo tenant

    Clones:
    - Procurement plans with requirements
    - Purchase orders with line items
    - Replenishment plans with items
    - Adjusts dates to recent timeframe

    Args:
        base_tenant_id: Template tenant UUID to clone from
        virtual_tenant_id: Target virtual tenant UUID
        demo_account_type: Type of demo account
        session_id: Originating session ID for tracing

    Returns:
        Cloning status and record counts
    """
    start_time = datetime.now(timezone.utc)

    # Parse session creation time for date adjustment
    if session_created_at:
        try:
            session_time = datetime.fromisoformat(session_created_at.replace('Z', '+00:00'))
        except (ValueError, AttributeError):
            session_time = start_time
    else:
        session_time = start_time

    logger.info(
        "Starting procurement data cloning",
        base_tenant_id=base_tenant_id,
        virtual_tenant_id=virtual_tenant_id,
        demo_account_type=demo_account_type,
        session_id=session_id,
        session_created_at=session_created_at
    )

    try:
        # Validate UUIDs
        base_uuid = uuid.UUID(base_tenant_id)
        virtual_uuid = uuid.UUID(virtual_tenant_id)

        # Track cloning statistics
        stats = {
            "procurement_plans": 0,
            "procurement_requirements": 0,
            "purchase_orders": 0,
            "purchase_order_items": 0,
            "replenishment_plans": 0,
            "replenishment_items": 0
        }

        # Clone Procurement Plans with Requirements
        result = await db.execute(
            select(ProcurementPlan).where(ProcurementPlan.tenant_id == base_uuid)
        )
        base_plans = result.scalars().all()

        logger.info(
            "Found procurement plans to clone",
            count=len(base_plans),
            base_tenant=str(base_uuid)
        )

        # Calculate date offset for procurement
        if base_plans:
            max_plan_date = max(plan.plan_date for plan in base_plans if plan.plan_date)
            today_date = date.today()
            days_diff = (today_date - max_plan_date).days
            plan_date_offset = timedelta(days=days_diff)
        else:
            plan_date_offset = timedelta(days=0)

        plan_id_map = {}

        for plan in base_plans:
            new_plan_id = uuid.uuid4()
            plan_id_map[plan.id] = new_plan_id

            new_plan = ProcurementPlan(
                id=new_plan_id,
                tenant_id=virtual_uuid,
                plan_number=f"PROC-{uuid.uuid4().hex[:8].upper()}",
                plan_date=plan.plan_date + plan_date_offset if plan.plan_date else None,
                plan_period_start=plan.plan_period_start + plan_date_offset if plan.plan_period_start else None,
                plan_period_end=plan.plan_period_end + plan_date_offset if plan.plan_period_end else None,
                planning_horizon_days=plan.planning_horizon_days,
                status=plan.status,
                plan_type=plan.plan_type,
                priority=plan.priority,
                business_model=plan.business_model,
                procurement_strategy=plan.procurement_strategy,
                total_requirements=plan.total_requirements,
                total_estimated_cost=plan.total_estimated_cost,
                total_approved_cost=plan.total_approved_cost,
                cost_variance=plan.cost_variance,
                created_at=session_time,
                updated_at=session_time
            )
            db.add(new_plan)
            stats["procurement_plans"] += 1

        # Clone Procurement Requirements
        for old_plan_id, new_plan_id in plan_id_map.items():
            result = await db.execute(
                select(ProcurementRequirement).where(ProcurementRequirement.plan_id == old_plan_id)
            )
            requirements = result.scalars().all()

            for req in requirements:
                new_req = ProcurementRequirement(
                    id=uuid.uuid4(),
                    plan_id=new_plan_id,
                    requirement_number=req.requirement_number,
                    product_id=req.product_id,
                    product_name=req.product_name,
                    product_sku=req.product_sku,
                    product_category=req.product_category,
                    product_type=req.product_type,
                    required_quantity=req.required_quantity,
                    unit_of_measure=req.unit_of_measure,
                    safety_stock_quantity=req.safety_stock_quantity,
                    total_quantity_needed=req.total_quantity_needed,
                    current_stock_level=req.current_stock_level,
                    reserved_stock=req.reserved_stock,
                    available_stock=req.available_stock,
                    net_requirement=req.net_requirement,
                    order_demand=req.order_demand,
                    production_demand=req.production_demand,
                    forecast_demand=req.forecast_demand,
                    buffer_demand=req.buffer_demand,
                    preferred_supplier_id=req.preferred_supplier_id,
                    backup_supplier_id=req.backup_supplier_id,
                    supplier_name=req.supplier_name,
                    supplier_lead_time_days=req.supplier_lead_time_days,
                    minimum_order_quantity=req.minimum_order_quantity,
                    estimated_unit_cost=req.estimated_unit_cost,
                    estimated_total_cost=req.estimated_total_cost,
                    last_purchase_cost=req.last_purchase_cost,
                    cost_variance=req.cost_variance,
                    required_by_date=req.required_by_date + plan_date_offset if req.required_by_date else None,
                    lead_time_buffer_days=req.lead_time_buffer_days,
                    suggested_order_date=req.suggested_order_date + plan_date_offset if req.suggested_order_date else None,
                    latest_order_date=req.latest_order_date + plan_date_offset if req.latest_order_date else None,
                    quality_specifications=req.quality_specifications,
                    special_requirements=req.special_requirements,
                    storage_requirements=req.storage_requirements,
                    shelf_life_days=req.shelf_life_days,
                    status=req.status,
                    priority=req.priority,
                    risk_level=req.risk_level,
                    purchase_order_id=req.purchase_order_id,
                    purchase_order_number=req.purchase_order_number,
                    ordered_quantity=req.ordered_quantity,
                    ordered_at=req.ordered_at,
                    expected_delivery_date=req.expected_delivery_date + plan_date_offset if req.expected_delivery_date else None,
                    actual_delivery_date=req.actual_delivery_date + plan_date_offset if req.actual_delivery_date else None,
                    received_quantity=req.received_quantity,
                    delivery_status=req.delivery_status,
                    fulfillment_rate=req.fulfillment_rate,
                    on_time_delivery=req.on_time_delivery,
                    quality_rating=req.quality_rating,
                    source_orders=req.source_orders,
                    source_production_batches=req.source_production_batches,
                    demand_analysis=req.demand_analysis,
                    approved_quantity=req.approved_quantity,
                    approved_cost=req.approved_cost,
                    approved_at=req.approved_at,
                    approved_by=req.approved_by,
                    procurement_notes=req.procurement_notes,
                    supplier_communication=req.supplier_communication,
                    requirement_metadata=req.requirement_metadata,
                    created_at=session_time,
                    updated_at=session_time
                )
                db.add(new_req)
                stats["procurement_requirements"] += 1

        # Clone Purchase Orders with Line Items
        result = await db.execute(
            select(PurchaseOrder).where(PurchaseOrder.tenant_id == base_uuid)
        )
        base_orders = result.scalars().all()

        logger.info(
            "Found purchase orders to clone",
            count=len(base_orders),
            base_tenant=str(base_uuid)
        )

        order_id_map = {}

        for order in base_orders:
            new_order_id = uuid.uuid4()
            order_id_map[order.id] = new_order_id

            # Adjust dates using demo_dates utility
            adjusted_order_date = adjust_date_for_demo(
                order.order_date, session_time, BASE_REFERENCE_DATE
            )
            adjusted_required_delivery = adjust_date_for_demo(
                order.required_delivery_date, session_time, BASE_REFERENCE_DATE
            )
            adjusted_estimated_delivery = adjust_date_for_demo(
                order.estimated_delivery_date, session_time, BASE_REFERENCE_DATE
            )
            adjusted_supplier_confirmation = adjust_date_for_demo(
                order.supplier_confirmation_date, session_time, BASE_REFERENCE_DATE
            )
            adjusted_approved_at = adjust_date_for_demo(
                order.approved_at, session_time, BASE_REFERENCE_DATE
            )
            adjusted_sent_to_supplier_at = adjust_date_for_demo(
                order.sent_to_supplier_at, session_time, BASE_REFERENCE_DATE
            )

            # Generate a system user UUID for audit fields (demo purposes)
            system_user_id = uuid.uuid4()

            new_order = PurchaseOrder(
                id=new_order_id,
                tenant_id=virtual_uuid,
                po_number=f"PO-{uuid.uuid4().hex[:8].upper()}",  # New PO number
                reference_number=order.reference_number,
                supplier_id=order.supplier_id,
                procurement_plan_id=plan_id_map.get(order.procurement_plan_id) if hasattr(order, 'procurement_plan_id') and order.procurement_plan_id else None,
                order_date=adjusted_order_date,
                required_delivery_date=adjusted_required_delivery,
                estimated_delivery_date=adjusted_estimated_delivery,
                status=order.status,
                priority=order.priority,
                subtotal=order.subtotal,
                tax_amount=order.tax_amount,
                discount_amount=order.discount_amount,
                shipping_cost=order.shipping_cost,
                total_amount=order.total_amount,
                currency=order.currency,
                delivery_address=order.delivery_address if hasattr(order, 'delivery_address') else None,
                delivery_instructions=order.delivery_instructions if hasattr(order, 'delivery_instructions') else None,
                delivery_contact=order.delivery_contact if hasattr(order, 'delivery_contact') else None,
                delivery_phone=order.delivery_phone if hasattr(order, 'delivery_phone') else None,
                requires_approval=order.requires_approval if hasattr(order, 'requires_approval') else False,
                approved_by=order.approved_by if hasattr(order, 'approved_by') else None,
                approved_at=adjusted_approved_at,
                rejection_reason=order.rejection_reason if hasattr(order, 'rejection_reason') else None,
                auto_approved=order.auto_approved if hasattr(order, 'auto_approved') else False,
                auto_approval_rule_id=order.auto_approval_rule_id if hasattr(order, 'auto_approval_rule_id') else None,
                sent_to_supplier_at=adjusted_sent_to_supplier_at,
                supplier_confirmation_date=adjusted_supplier_confirmation,
                supplier_reference=order.supplier_reference if hasattr(order, 'supplier_reference') else None,
                notes=order.notes if hasattr(order, 'notes') else None,
                internal_notes=order.internal_notes if hasattr(order, 'internal_notes') else None,
                terms_and_conditions=order.terms_and_conditions if hasattr(order, 'terms_and_conditions') else None,
                created_at=session_time,
                updated_at=session_time,
                created_by=system_user_id,
                updated_by=system_user_id
            )
            db.add(new_order)
            stats["purchase_orders"] += 1

        # Clone Purchase Order Items
        for old_order_id, new_order_id in order_id_map.items():
            result = await db.execute(
                select(PurchaseOrderItem).where(PurchaseOrderItem.purchase_order_id == old_order_id)
            )
            order_items = result.scalars().all()

            for item in order_items:
                new_item = PurchaseOrderItem(
                    id=uuid.uuid4(),
                    tenant_id=virtual_uuid,
                    purchase_order_id=new_order_id,
                    procurement_requirement_id=item.procurement_requirement_id if hasattr(item, 'procurement_requirement_id') else None,
                    inventory_product_id=item.inventory_product_id,
                    product_code=item.product_code if hasattr(item, 'product_code') else None,
                    product_name=item.product_name,
                    supplier_price_list_id=item.supplier_price_list_id if hasattr(item, 'supplier_price_list_id') else None,
                    ordered_quantity=item.ordered_quantity,
                    unit_of_measure=item.unit_of_measure,
                    unit_price=item.unit_price,
                    line_total=item.line_total,
                    received_quantity=item.received_quantity if hasattr(item, 'received_quantity') else 0,
                    remaining_quantity=item.remaining_quantity if hasattr(item, 'remaining_quantity') else item.ordered_quantity,
                    quality_requirements=item.quality_requirements if hasattr(item, 'quality_requirements') else None,
                    item_notes=item.item_notes if hasattr(item, 'item_notes') else None,
                    created_at=session_time,
                    updated_at=session_time
                )
                db.add(new_item)
                stats["purchase_order_items"] += 1

        # Clone Replenishment Plans with Items
        result = await db.execute(
            select(ReplenishmentPlan).where(ReplenishmentPlan.tenant_id == base_uuid)
        )
        base_replenishment_plans = result.scalars().all()

        logger.info(
            "Found replenishment plans to clone",
            count=len(base_replenishment_plans),
            base_tenant=str(base_uuid)
        )

        replan_id_map = {}

        for replan in base_replenishment_plans:
            new_replan_id = uuid.uuid4()
            replan_id_map[replan.id] = new_replan_id

            new_replan = ReplenishmentPlan(
                id=new_replan_id,
                tenant_id=virtual_uuid,
                plan_number=f"REPL-{uuid.uuid4().hex[:8].upper()}",
                plan_date=replan.plan_date + plan_date_offset if replan.plan_date else None,
                plan_period_start=replan.plan_period_start + plan_date_offset if replan.plan_period_start else None,
                plan_period_end=replan.plan_period_end + plan_date_offset if replan.plan_period_end else None,
                planning_horizon_days=replan.planning_horizon_days,
                status=replan.status,
                plan_type=replan.plan_type,
                priority=replan.priority,
                business_model=replan.business_model,
                total_items=replan.total_items,
                total_estimated_cost=replan.total_estimated_cost,
                created_at=session_time,
                updated_at=session_time
            )
            db.add(new_replan)
            stats["replenishment_plans"] += 1

        # Clone Replenishment Plan Items
        for old_replan_id, new_replan_id in replan_id_map.items():
            result = await db.execute(
                select(ReplenishmentPlanItem).where(ReplenishmentPlanItem.plan_id == old_replan_id)
            )
            replan_items = result.scalars().all()

            for item in replan_items:
                new_item = ReplenishmentPlanItem(
                    id=uuid.uuid4(),
                    plan_id=new_replan_id,
                    product_id=item.product_id,
                    product_name=item.product_name,
                    product_sku=item.product_sku,
                    required_quantity=item.required_quantity,
                    unit_of_measure=item.unit_of_measure,
                    current_stock_level=item.current_stock_level,
                    safety_stock_quantity=item.safety_stock_quantity,
                    suggested_order_quantity=item.suggested_order_quantity,
                    supplier_id=item.supplier_id,
                    supplier_name=item.supplier_name,
                    estimated_delivery_days=item.estimated_delivery_days,
                    required_by_date=item.required_by_date + plan_date_offset if item.required_by_date else None,
                    status=item.status,
                    priority=item.priority,
                    notes=item.notes,
                    created_at=session_time,
                    updated_at=session_time
                )
                db.add(new_item)
                stats["replenishment_items"] += 1

        # Commit cloned data
        await db.commit()

        total_records = sum(stats.values())
        duration_ms = int((datetime.now(timezone.utc) - start_time).total_seconds() * 1000)

        logger.info(
            "Procurement data cloning completed",
            virtual_tenant_id=virtual_tenant_id,
            total_records=total_records,
            stats=stats,
            duration_ms=duration_ms
        )

        return {
            "service": "procurement",
            "status": "completed",
            "records_cloned": total_records,
            "duration_ms": duration_ms,
            "details": stats
        }

    except ValueError as e:
        logger.error("Invalid UUID format", error=str(e))
        raise HTTPException(status_code=400, detail=f"Invalid UUID: {str(e)}")

    except Exception as e:
        logger.error(
            "Failed to clone procurement data",
            error=str(e),
            virtual_tenant_id=virtual_tenant_id,
            exc_info=True
        )

        # Rollback on error
        await db.rollback()

        return {
            "service": "procurement",
            "status": "failed",
            "records_cloned": 0,
            "duration_ms": int((datetime.now(timezone.utc) - start_time).total_seconds() * 1000),
            "error": str(e)
        }


@router.get("/clone/health")
async def clone_health_check(_: bool = Depends(verify_internal_api_key)):
    """
    Health check for internal cloning endpoint
    Used by orchestrator to verify service availability
    """
    return {
        "service": "procurement",
        "clone_endpoint": "available",
        "version": "2.0.0"
    }


@router.delete("/tenant/{virtual_tenant_id}")
async def delete_demo_data(
    virtual_tenant_id: str,
    db: AsyncSession = Depends(get_db),
    _: bool = Depends(verify_internal_api_key)
):
    """Delete all procurement data for a virtual demo tenant"""
    logger.info("Deleting procurement data for virtual tenant", virtual_tenant_id=virtual_tenant_id)
    start_time = datetime.now(timezone.utc)

    try:
        virtual_uuid = uuid.UUID(virtual_tenant_id)

        # Count records
        po_count = await db.scalar(select(func.count(PurchaseOrder.id)).where(PurchaseOrder.tenant_id == virtual_uuid))
        item_count = await db.scalar(select(func.count(PurchaseOrderItem.id)).where(PurchaseOrderItem.tenant_id == virtual_uuid))
        plan_count = await db.scalar(select(func.count(ProcurementPlan.id)).where(ProcurementPlan.tenant_id == virtual_uuid))
        req_count = await db.scalar(select(func.count(ProcurementRequirement.id)).where(ProcurementRequirement.tenant_id == virtual_uuid))
        replan_count = await db.scalar(select(func.count(ReplenishmentPlan.id)).where(ReplenishmentPlan.tenant_id == virtual_uuid))
        replan_item_count = await db.scalar(select(func.count(ReplenishmentPlanItem.id)).where(ReplenishmentPlanItem.tenant_id == virtual_uuid))

        # Delete in order (respecting foreign key constraints)
        await db.execute(delete(PurchaseOrderItem).where(PurchaseOrderItem.tenant_id == virtual_uuid))
        await db.execute(delete(PurchaseOrder).where(PurchaseOrder.tenant_id == virtual_uuid))
        await db.execute(delete(ProcurementRequirement).where(ProcurementRequirement.tenant_id == virtual_uuid))
        await db.execute(delete(ProcurementPlan).where(ProcurementPlan.tenant_id == virtual_uuid))
        await db.execute(delete(ReplenishmentPlanItem).where(ReplenishmentPlanItem.tenant_id == virtual_uuid))
        await db.execute(delete(ReplenishmentPlan).where(ReplenishmentPlan.tenant_id == virtual_uuid))
        await db.commit()

        duration_ms = int((datetime.now(timezone.utc) - start_time).total_seconds() * 1000)
        logger.info("Procurement data deleted successfully", virtual_tenant_id=virtual_tenant_id, duration_ms=duration_ms)

        return {
            "service": "procurement",
            "status": "deleted",
            "virtual_tenant_id": virtual_tenant_id,
            "records_deleted": {
                "purchase_orders": po_count,
                "purchase_order_items": item_count,
                "procurement_plans": plan_count,
                "procurement_requirements": req_count,
                "replenishment_plans": replan_count,
                "replenishment_items": replan_item_count,
                "total": po_count + item_count + plan_count + req_count + replan_count + replan_item_count
            },
            "duration_ms": duration_ms
        }
    except Exception as e:
        logger.error("Failed to delete procurement data", error=str(e), exc_info=True)
        await db.rollback()
        raise HTTPException(status_code=500, detail=str(e))
