"""
Internal Demo Cloning API for Inventory Service
Handles internal demo data cloning operations
"""

from fastapi import APIRouter, Depends, HTTPException, Header
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, delete
from typing import Optional
import structlog
import json
from pathlib import Path
from datetime import datetime, timezone, timedelta
import uuid
from uuid import UUID

from app.core.database import get_db
from app.core.config import settings
from app.models import Ingredient, Stock, ProductType
from shared.utils.demo_dates import adjust_date_for_demo, resolve_time_marker, calculate_edge_case_times

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


def parse_date_field(date_value, session_time: datetime, field_name: str = "date") -> Optional[datetime]:
    """
    Parse date field, handling both ISO strings and BASE_TS markers.

    Supports:
    - BASE_TS markers: "BASE_TS + 1h30m", "BASE_TS - 2d"
    - ISO 8601 strings: "2025-01-15T06:00:00Z"
    - None values (returns None)

    Returns timezone-aware datetime or None.
    """
    if not date_value:
        return None

    # Check if it's a BASE_TS marker
    if isinstance(date_value, str) and date_value.startswith("BASE_TS"):
        try:
            return resolve_time_marker(date_value, session_time)
        except ValueError as e:
            logger.warning(
                f"Invalid BASE_TS marker in {field_name}",
                marker=date_value,
                error=str(e)
            )
            return None

    # Handle regular ISO date strings
    try:
        if isinstance(date_value, str):
            original_date = datetime.fromisoformat(date_value.replace('Z', '+00:00'))
        elif hasattr(date_value, 'isoformat'):
            original_date = date_value
        else:
            logger.warning(f"Unsupported date format in {field_name}", date_value=date_value)
            return None

        return adjust_date_for_demo(original_date, session_time)
    except (ValueError, AttributeError) as e:
        logger.warning(
            f"Invalid date format in {field_name}",
            date_value=date_value,
            error=str(e)
        )
        return None


@router.post("/clone")
async def clone_demo_data_internal(
    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)
):
    """
    Clone inventory service data for a virtual demo tenant

    This endpoint creates fresh demo data by:
    1. Loading seed data from JSON files
    2. Applying XOR-based ID transformation
    3. Adjusting dates relative to session creation time
    4. Creating records in the virtual tenant

    Args:
        base_tenant_id: Template tenant UUID (for reference)
        virtual_tenant_id: Target virtual tenant UUID
        demo_account_type: Type of demo account
        session_id: Originating session ID for tracing
        session_created_at: Session creation timestamp for date adjustment
        db: Database session

    Returns:
        Dictionary with cloning results

    Raises:
        HTTPException: On validation or cloning errors
    """
    start_time = datetime.now(timezone.utc)
    
    try:
        # Validate UUIDs
        virtual_uuid = UUID(virtual_tenant_id)
        
        # 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
        
        # Debug logging for UUID values
        logger.debug("Received UUID values", base_tenant_id=base_tenant_id, virtual_tenant_id=virtual_tenant_id)

        if not all([base_tenant_id, virtual_tenant_id, session_id]):
            raise HTTPException(
                status_code=400,
                detail="Missing required parameters: base_tenant_id, virtual_tenant_id, session_id"
            )
        
        # Validate UUID format before processing
        try:
            UUID(base_tenant_id)
            UUID(virtual_tenant_id)
        except ValueError as e:
            logger.error("Invalid UUID format in request", 
                        base_tenant_id=base_tenant_id, 
                        virtual_tenant_id=virtual_tenant_id, 
                        error=str(e))
            raise HTTPException(
                status_code=400,
                detail=f"Invalid UUID format: {str(e)}"
            )

        # Parse session creation time
        if session_created_at:
            try:
                session_created_at_parsed = datetime.fromisoformat(session_created_at.replace('Z', '+00:00'))
            except (ValueError, AttributeError):
                session_created_at_parsed = datetime.now(timezone.utc)
        else:
            session_created_at_parsed = datetime.now(timezone.utc)
        
        logger.info(
            "Starting inventory data cloning with date adjustment",
            base_tenant_id=base_tenant_id,
            virtual_tenant_id=virtual_tenant_id,
            demo_account_type=demo_account_type,
            session_id=session_id,
            session_time=session_created_at_parsed.isoformat()
        )

        # Load seed data from JSON files
        from shared.utils.seed_data_paths import get_seed_data_path

        if demo_account_type == "professional":
            json_file = get_seed_data_path("professional", "03-inventory.json")
        elif demo_account_type == "enterprise":
            json_file = get_seed_data_path("enterprise", "03-inventory.json")
        elif demo_account_type == "enterprise_child":
            json_file = get_seed_data_path("enterprise", "03-inventory.json", child_id=base_tenant_id)
        else:
            raise ValueError(f"Invalid demo account type: {demo_account_type}")

        # Load JSON data
        with open(json_file, 'r', encoding='utf-8') as f:
            seed_data = json.load(f)

        # Check if data already exists for this virtual tenant (idempotency)
        existing_check = await db.execute(
            select(Ingredient).where(Ingredient.tenant_id == virtual_tenant_id).limit(1)
        )
        existing_ingredient = existing_check.scalar_one_or_none()

        if existing_ingredient:
            logger.warning(
                "Demo data already exists, skipping clone",
                virtual_tenant_id=virtual_tenant_id
            )
            return {
                "status": "skipped",
                "reason": "Data already exists",
                "records_cloned": 0
            }

        # Transform and insert data
        records_cloned = 0

        # Clone ingredients
        for ingredient_data in seed_data.get('ingredients', []):
            # Transform ID
            from shared.utils.demo_id_transformer import transform_id
            try:
                ingredient_uuid = UUID(ingredient_data['id'])
                tenant_uuid = UUID(virtual_tenant_id)
                transformed_id = transform_id(ingredient_data['id'], tenant_uuid)
            except ValueError as e:
                logger.error("Failed to parse UUIDs for ID transformation",
                            ingredient_id=ingredient_data['id'],
                            virtual_tenant_id=virtual_tenant_id,
                            error=str(e))
                raise HTTPException(
                    status_code=400,
                    detail=f"Invalid UUID format in ingredient data: {str(e)}"
                )

            # Transform dates using standardized helper
            ingredient_data['created_at'] = parse_date_field(
                ingredient_data.get('created_at'), session_time, 'created_at'
            ) or session_time
            ingredient_data['updated_at'] = parse_date_field(
                ingredient_data.get('updated_at'), session_time, 'updated_at'
            ) or session_time

            # Map category field to ingredient_category enum
            if 'category' in ingredient_data:
                category_value = ingredient_data.pop('category')
                # Convert category string to IngredientCategory enum
                from app.models.inventory import IngredientCategory
                try:
                    ingredient_data['ingredient_category'] = IngredientCategory[category_value.upper()]
                except KeyError:
                    # If category not found in enum, use OTHER
                    ingredient_data['ingredient_category'] = IngredientCategory.OTHER

            # Map unit_of_measure string to enum
            if 'unit_of_measure' in ingredient_data:
                from app.models.inventory import UnitOfMeasure
                unit_mapping = {
                    'kilograms': UnitOfMeasure.KILOGRAMS,
                    'grams': UnitOfMeasure.GRAMS,
                    'liters': UnitOfMeasure.LITERS,
                    'milliliters': UnitOfMeasure.MILLILITERS,
                    'units': UnitOfMeasure.UNITS,
                    'pieces': UnitOfMeasure.PIECES,
                    'packages': UnitOfMeasure.PACKAGES,
                    'bags': UnitOfMeasure.BAGS,
                    'boxes': UnitOfMeasure.BOXES
                }

                # Also support uppercase versions
                unit_mapping.update({
                    'KILOGRAMS': UnitOfMeasure.KILOGRAMS,
                    'GRAMS': UnitOfMeasure.GRAMS,
                    'LITERS': UnitOfMeasure.LITERS,
                    'MILLILITERS': UnitOfMeasure.MILLILITERS,
                    'UNITS': UnitOfMeasure.UNITS,
                    'PIECES': UnitOfMeasure.PIECES,
                    'PACKAGES': UnitOfMeasure.PACKAGES,
                    'BAGS': UnitOfMeasure.BAGS,
                    'BOXES': UnitOfMeasure.BOXES
                })

                unit_str = ingredient_data['unit_of_measure']
                if unit_str in unit_mapping:
                    ingredient_data['unit_of_measure'] = unit_mapping[unit_str]
                else:
                    # Default to units if not found
                    ingredient_data['unit_of_measure'] = UnitOfMeasure.UNITS
                    logger.warning("Unknown unit_of_measure, defaulting to UNITS",
                                 original_unit=unit_str)

            # Note: All seed data fields now match the model schema exactly
            # No field filtering needed

            # Remove original id and tenant_id from ingredient_data to avoid conflict
            ingredient_data.pop('id', None)
            ingredient_data.pop('tenant_id', None)

            # Create ingredient
            ingredient = Ingredient(
                id=str(transformed_id),
                tenant_id=str(virtual_tenant_id),
                **ingredient_data
            )
            db.add(ingredient)
            records_cloned += 1

        # Commit ingredients before creating stock to ensure foreign key references exist
        await db.flush()  # Use flush instead of commit to maintain transaction while continuing

        # Clone stock batches
        for stock_data in seed_data.get('stock', []):
            # Transform ID - handle both UUID and string IDs
            from shared.utils.demo_id_transformer import transform_id
            try:
                # Try to parse as UUID first
                stock_uuid = UUID(stock_data['id'])
                tenant_uuid = UUID(virtual_tenant_id)
                transformed_id = transform_id(stock_data['id'], tenant_uuid)
            except ValueError:
                # If not a UUID, generate a deterministic UUID from the string ID
                import hashlib
                stock_id_string = stock_data['id']
                tenant_uuid = UUID(virtual_tenant_id)
                
                # Create a deterministic UUID from the string ID and tenant ID
                combined = f"{stock_id_string}-{tenant_uuid}"
                hash_obj = hashlib.sha256(combined.encode('utf-8'))
                transformed_id = UUID(hash_obj.hexdigest()[:32])
                
                logger.info("Generated UUID for non-UUID stock ID",
                           original_id=stock_id_string,
                           generated_id=str(transformed_id))
            
            # Transform dates using standardized helper
            stock_data['received_date'] = parse_date_field(
                stock_data.get('received_date'), session_time, 'received_date'
            )
            stock_data['expiration_date'] = parse_date_field(
                stock_data.get('expiration_date'), session_time, 'expiration_date'
            )
            stock_data['best_before_date'] = parse_date_field(
                stock_data.get('best_before_date'), session_time, 'best_before_date'
            )
            stock_data['created_at'] = parse_date_field(
                stock_data.get('created_at'), session_time, 'created_at'
            ) or session_time
            stock_data['updated_at'] = parse_date_field(
                stock_data.get('updated_at'), session_time, 'updated_at'
            ) or session_time
            
            # Remove original id and tenant_id from stock_data to avoid conflict
            stock_data.pop('id', None)
            stock_data.pop('tenant_id', None)
            # Remove notes field as it doesn't exist in the Stock model
            stock_data.pop('notes', None)

            # Transform ingredient_id to match transformed ingredient IDs
            if 'ingredient_id' in stock_data:
                ingredient_id_str = stock_data['ingredient_id']
                try:
                    ingredient_uuid = UUID(ingredient_id_str)
                    transformed_ingredient_id = transform_id(ingredient_id_str, tenant_uuid)
                    stock_data['ingredient_id'] = str(transformed_ingredient_id)
                except ValueError as e:
                    logger.error("Failed to transform ingredient_id",
                                original_ingredient_id=ingredient_id_str,
                                error=str(e))
                    raise HTTPException(
                        status_code=400,
                        detail=f"Invalid ingredient_id format: {str(e)}"
                    )
            
            # Transform supplier_id if present
            if 'supplier_id' in stock_data:
                supplier_id_str = stock_data['supplier_id']
                try:
                    supplier_uuid = UUID(supplier_id_str)
                    transformed_supplier_id = transform_id(supplier_id_str, tenant_uuid)
                    stock_data['supplier_id'] = str(transformed_supplier_id)
                except ValueError as e:
                    logger.error("Failed to transform supplier_id",
                                original_supplier_id=supplier_id_str,
                                error=str(e))
                    raise HTTPException(
                        status_code=400,
                        detail=f"Invalid supplier_id format: {str(e)}"
                    )
            
            # Create stock batch
            stock = Stock(
                id=str(transformed_id),
                tenant_id=str(virtual_tenant_id),
                **stock_data
            )
            db.add(stock)
            records_cloned += 1

        # Clone stock movements (for waste tracking and sustainability metrics)
        from app.models.inventory import StockMovement, StockMovementType

        for movement_data in seed_data.get('stock_movements', []):
            # Transform ID
            from shared.utils.demo_id_transformer import transform_id
            try:
                movement_uuid = UUID(movement_data['id'])
                tenant_uuid = UUID(virtual_tenant_id)
                transformed_id = transform_id(movement_data['id'], tenant_uuid)
            except ValueError:
                import hashlib
                movement_id_string = movement_data['id']
                tenant_uuid = UUID(virtual_tenant_id)
                combined = f"{movement_id_string}-{tenant_uuid}"
                hash_obj = hashlib.sha256(combined.encode('utf-8'))
                transformed_id = UUID(hash_obj.hexdigest()[:32])

            # Transform dates
            movement_data['movement_date'] = parse_date_field(
                movement_data.get('movement_date'), session_time, 'movement_date'
            ) or session_time
            movement_data['created_at'] = parse_date_field(
                movement_data.get('created_at'), session_time, 'created_at'
            ) or session_time

            # Transform related IDs
            if 'ingredient_id' in movement_data:
                ingredient_id_str = movement_data['ingredient_id']
                try:
                    transformed_ingredient_id = transform_id(ingredient_id_str, tenant_uuid)
                    movement_data['ingredient_id'] = str(transformed_ingredient_id)
                except ValueError as e:
                    logger.error("Failed to transform ingredient_id in movement",
                                original_id=ingredient_id_str, error=str(e))
                    raise HTTPException(status_code=400, detail=f"Invalid ingredient_id: {str(e)}")

            if 'stock_id' in movement_data and movement_data['stock_id']:
                stock_id_str = movement_data['stock_id']
                try:
                    transformed_stock_id = transform_id(stock_id_str, tenant_uuid)
                    movement_data['stock_id'] = str(transformed_stock_id)
                except ValueError:
                    # If stock_id doesn't exist or can't be transformed, set to None
                    movement_data['stock_id'] = None

            if 'supplier_id' in movement_data and movement_data['supplier_id']:
                supplier_id_str = movement_data['supplier_id']
                try:
                    transformed_supplier_id = transform_id(supplier_id_str, tenant_uuid)
                    movement_data['supplier_id'] = str(transformed_supplier_id)
                except ValueError:
                    movement_data['supplier_id'] = None

            if 'created_by' in movement_data and movement_data['created_by']:
                created_by_str = movement_data['created_by']
                try:
                    transformed_created_by = transform_id(created_by_str, tenant_uuid)
                    movement_data['created_by'] = str(transformed_created_by)
                except ValueError:
                    movement_data['created_by'] = None

            # Remove original id and tenant_id
            movement_data.pop('id', None)
            movement_data.pop('tenant_id', None)

            # Create stock movement
            stock_movement = StockMovement(
                id=str(transformed_id),
                tenant_id=str(virtual_tenant_id),
                **movement_data
            )
            db.add(stock_movement)
            records_cloned += 1

        # Note: Edge cases are now handled exclusively through JSON seed data
        # The seed data files already contain comprehensive edge cases including:
        # - Low stock items below reorder points
        # - Items expiring soon
        # - Freshly received stock
        # - Waste movements for sustainability tracking
        # This ensures standardization and single source of truth for demo data

        logger.info(
            "Edge cases handled by JSON seed data - no manual creation needed",
            seed_data_edge_cases="low_stock, expiring_soon, fresh_stock, waste_movements"
        )

        await db.commit()

        duration_ms = int((datetime.now(timezone.utc) - start_time).total_seconds() * 1000)

        logger.info(
            "Inventory data cloned successfully",
            virtual_tenant_id=virtual_tenant_id,
            records_cloned=records_cloned,
            duration_ms=duration_ms,
            ingredients_cloned=len(seed_data.get('ingredients', [])),
            stock_batches_cloned=len(seed_data.get('stock', [])),
            stock_movements_cloned=len(seed_data.get('stock_movements', []))
        )

        return {
            "service": "inventory",
            "status": "completed",
            "records_cloned": records_cloned,
            "duration_ms": duration_ms,
            "details": {
                "ingredients": len(seed_data.get('ingredients', [])),
                "stock": len(seed_data.get('stock', [])),
                "virtual_tenant_id": str(virtual_tenant_id)
            }
        }

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

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

        # Rollback on error
        await db.rollback()

        return {
            "service": "inventory",
            "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():
    """
    Health check for internal cloning endpoint
    Used by orchestrator to verify service availability
    """
    return {
        "service": "inventory",
        "clone_endpoint": "available",
        "version": "2.0.0"
    }


@router.delete("/tenant/{virtual_tenant_id}")
async def delete_demo_tenant_data(
    virtual_tenant_id: UUID,
    db: AsyncSession = Depends(get_db)
):
    """
    Delete all demo data for a virtual tenant.
    This endpoint is idempotent - safe to call multiple times.
    """
    start_time = datetime.now(timezone.utc)
    
    from app.models.inventory import StockMovement

    records_deleted = {
        "stock_movements": 0,
        "stock": 0,
        "ingredients": 0,
        "total": 0
    }

    try:
        # Delete in reverse dependency order

        # 1. Delete stock movements (depends on stock and ingredients)
        result = await db.execute(
            delete(StockMovement)
            .where(StockMovement.tenant_id == virtual_tenant_id)
        )
        records_deleted["stock_movements"] = result.rowcount

        # 2. Delete stock batches (depends on ingredients)
        result = await db.execute(
            delete(Stock)
            .where(Stock.tenant_id == virtual_tenant_id)
        )
        records_deleted["stock"] = result.rowcount

        # 3. Delete ingredients
        result = await db.execute(
            delete(Ingredient)
            .where(Ingredient.tenant_id == virtual_tenant_id)
        )
        records_deleted["ingredients"] = result.rowcount

        records_deleted["total"] = records_deleted["stock_movements"] + records_deleted["stock"] + records_deleted["ingredients"]

        await db.commit()

        logger.info(
            "demo_data_deleted",
            service="inventory",
            virtual_tenant_id=str(virtual_tenant_id),
            records_deleted=records_deleted
        )

        return {
            "service": "inventory",
            "status": "deleted",
            "virtual_tenant_id": str(virtual_tenant_id),
            "records_deleted": records_deleted,
            "duration_ms": int((datetime.now(timezone.utc) - start_time).total_seconds() * 1000)
        }

    except Exception as e:
        await db.rollback()
        logger.error(
            "demo_data_deletion_failed",
            service="inventory",
            virtual_tenant_id=str(virtual_tenant_id),
            error=str(e)
        )
        raise HTTPException(
            status_code=500,
            detail=f"Failed to delete demo data: {str(e)}"
        )