from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session, joinedload
from sqlalchemy import text
from typing import Optional, List
from datetime import datetime, date
from pydantic import BaseModel
from decimal import Decimal

from ..database import get_db
from ..models import (
    TubingCategory, TubingSpecification, TubingManufacturer, 
    TubingProduct, MaterialTubingMapping, Material
)

router = APIRouter()

# ================================
# Pydantic 모델들
# ================================

class TubingCategoryResponse(BaseModel):
    id: int
    category_code: str
    category_name: str
    description: Optional[str] = None
    is_active: bool
    created_at: datetime

    class Config:
        from_attributes = True

class TubingManufacturerResponse(BaseModel):
    id: int
    manufacturer_code: str
    manufacturer_name: str
    country: Optional[str] = None
    website: Optional[str] = None
    is_active: bool

    class Config:
        from_attributes = True

class TubingSpecificationResponse(BaseModel):
    id: int
    spec_code: str
    spec_name: str
    category_name: Optional[str] = None
    outer_diameter_mm: Optional[float] = None
    wall_thickness_mm: Optional[float] = None
    inner_diameter_mm: Optional[float] = None
    material_grade: Optional[str] = None
    material_standard: Optional[str] = None
    max_pressure_bar: Optional[float] = None
    max_temperature_c: Optional[float] = None
    min_temperature_c: Optional[float] = None
    standard_length_m: Optional[float] = None
    surface_finish: Optional[str] = None
    is_active: bool

    class Config:
        from_attributes = True

class TubingProductResponse(BaseModel):
    id: int
    specification_id: int
    manufacturer_id: int
    manufacturer_part_number: str
    manufacturer_product_name: Optional[str] = None
    spec_name: Optional[str] = None
    manufacturer_name: Optional[str] = None
    list_price: Optional[float] = None
    currency: Optional[str] = 'KRW'
    lead_time_days: Optional[int] = None
    availability_status: Optional[str] = None
    datasheet_url: Optional[str] = None
    notes: Optional[str] = None
    is_active: bool

    class Config:
        from_attributes = True

class TubingProductCreate(BaseModel):
    specification_id: int
    manufacturer_id: int
    manufacturer_part_number: str
    manufacturer_product_name: Optional[str] = None
    list_price: Optional[float] = None
    currency: str = 'KRW'
    lead_time_days: Optional[int] = None
    minimum_order_qty: Optional[float] = None
    standard_packaging_qty: Optional[float] = None
    availability_status: Optional[str] = None
    datasheet_url: Optional[str] = None
    catalog_page: Optional[str] = None
    notes: Optional[str] = None

class MaterialTubingMappingCreate(BaseModel):
    material_id: int
    tubing_product_id: int
    confidence_score: Optional[float] = None
    mapping_method: str = 'manual'
    required_length_m: Optional[float] = None
    calculated_quantity: Optional[float] = None
    notes: Optional[str] = None

# ================================
# API 엔드포인트들
# ================================

@router.get("/categories", response_model=List[TubingCategoryResponse])
async def get_tubing_categories(
    skip: int = Query(0, ge=0),
    limit: int = Query(100, ge=1, le=1000),
    db: Session = Depends(get_db)
):
    """Tubing 카테고리 목록 조회"""
    try:
        categories = db.query(TubingCategory)\
            .filter(TubingCategory.is_active == True)\
            .offset(skip)\
            .limit(limit)\
            .all()
        
        return categories
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"카테고리 조회 실패: {str(e)}")

@router.get("/manufacturers", response_model=List[TubingManufacturerResponse])
async def get_tubing_manufacturers(
    skip: int = Query(0, ge=0),
    limit: int = Query(100, ge=1, le=1000),
    search: Optional[str] = Query(None),
    country: Optional[str] = Query(None),
    db: Session = Depends(get_db)
):
    """Tubing 제조사 목록 조회"""
    try:
        query = db.query(TubingManufacturer)\
            .filter(TubingManufacturer.is_active == True)
        
        if search:
            query = query.filter(
                TubingManufacturer.manufacturer_name.ilike(f"%{search}%") |
                TubingManufacturer.manufacturer_code.ilike(f"%{search}%")
            )
        
        if country:
            query = query.filter(TubingManufacturer.country.ilike(f"%{country}%"))
        
        manufacturers = query.offset(skip).limit(limit).all()
        return manufacturers
        
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"제조사 조회 실패: {str(e)}")

@router.get("/specifications", response_model=List[TubingSpecificationResponse])
async def get_tubing_specifications(
    skip: int = Query(0, ge=0),
    limit: int = Query(100, ge=1, le=1000),
    category_id: Optional[int] = Query(None),
    material_grade: Optional[str] = Query(None),
    outer_diameter_min: Optional[float] = Query(None),
    outer_diameter_max: Optional[float] = Query(None),
    search: Optional[str] = Query(None),
    db: Session = Depends(get_db)
):
    """Tubing 규격 목록 조회"""
    try:
        query = db.query(TubingSpecification)\
            .options(joinedload(TubingSpecification.category))\
            .filter(TubingSpecification.is_active == True)
        
        if category_id:
            query = query.filter(TubingSpecification.category_id == category_id)
        
        if material_grade:
            query = query.filter(TubingSpecification.material_grade.ilike(f"%{material_grade}%"))
        
        if outer_diameter_min:
            query = query.filter(TubingSpecification.outer_diameter_mm >= outer_diameter_min)
            
        if outer_diameter_max:
            query = query.filter(TubingSpecification.outer_diameter_mm <= outer_diameter_max)
        
        if search:
            query = query.filter(
                TubingSpecification.spec_name.ilike(f"%{search}%") |
                TubingSpecification.spec_code.ilike(f"%{search}%") |
                TubingSpecification.material_grade.ilike(f"%{search}%")
            )
        
        specifications = query.offset(skip).limit(limit).all()
        
        # 응답 데이터 변환
        result = []
        for spec in specifications:
            spec_dict = {
                "id": spec.id,
                "spec_code": spec.spec_code,
                "spec_name": spec.spec_name,
                "category_name": spec.category.category_name if spec.category else None,
                "outer_diameter_mm": float(spec.outer_diameter_mm) if spec.outer_diameter_mm else None,
                "wall_thickness_mm": float(spec.wall_thickness_mm) if spec.wall_thickness_mm else None,
                "inner_diameter_mm": float(spec.inner_diameter_mm) if spec.inner_diameter_mm else None,
                "material_grade": spec.material_grade,
                "material_standard": spec.material_standard,
                "max_pressure_bar": float(spec.max_pressure_bar) if spec.max_pressure_bar else None,
                "max_temperature_c": float(spec.max_temperature_c) if spec.max_temperature_c else None,
                "min_temperature_c": float(spec.min_temperature_c) if spec.min_temperature_c else None,
                "standard_length_m": float(spec.standard_length_m) if spec.standard_length_m else None,
                "surface_finish": spec.surface_finish,
                "is_active": spec.is_active
            }
            result.append(spec_dict)
        
        return result
        
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"규격 조회 실패: {str(e)}")

@router.get("/products", response_model=List[TubingProductResponse])
async def get_tubing_products(
    skip: int = Query(0, ge=0),
    limit: int = Query(100, ge=1, le=1000),
    specification_id: Optional[int] = Query(None),
    manufacturer_id: Optional[int] = Query(None),
    search: Optional[str] = Query(None),
    db: Session = Depends(get_db)
):
    """Tubing 제품 목록 조회"""
    try:
        query = db.query(TubingProduct)\
            .options(
                joinedload(TubingProduct.specification),
                joinedload(TubingProduct.manufacturer)
            )\
            .filter(TubingProduct.is_active == True)
        
        if specification_id:
            query = query.filter(TubingProduct.specification_id == specification_id)
            
        if manufacturer_id:
            query = query.filter(TubingProduct.manufacturer_id == manufacturer_id)
        
        if search:
            query = query.filter(
                TubingProduct.manufacturer_part_number.ilike(f"%{search}%") |
                TubingProduct.manufacturer_product_name.ilike(f"%{search}%")
            )
        
        products = query.offset(skip).limit(limit).all()
        
        # 응답 데이터 변환
        result = []
        for product in products:
            product_dict = {
                "id": product.id,
                "specification_id": product.specification_id,
                "manufacturer_id": product.manufacturer_id,
                "manufacturer_part_number": product.manufacturer_part_number,
                "manufacturer_product_name": product.manufacturer_product_name,
                "spec_name": product.specification.spec_name if product.specification else None,
                "manufacturer_name": product.manufacturer.manufacturer_name if product.manufacturer else None,
                "list_price": float(product.list_price) if product.list_price else None,
                "currency": product.currency,
                "lead_time_days": product.lead_time_days,
                "availability_status": product.availability_status,
                "datasheet_url": product.datasheet_url,
                "notes": product.notes,
                "is_active": product.is_active
            }
            result.append(product_dict)
        
        return result
        
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"제품 조회 실패: {str(e)}")

@router.post("/products", response_model=TubingProductResponse)
async def create_tubing_product(
    product_data: TubingProductCreate,
    db: Session = Depends(get_db)
):
    """새 Tubing 제품 등록"""
    try:
        # 중복 확인
        existing = db.query(TubingProduct)\
            .filter(
                TubingProduct.specification_id == product_data.specification_id,
                TubingProduct.manufacturer_id == product_data.manufacturer_id,
                TubingProduct.manufacturer_part_number == product_data.manufacturer_part_number
            ).first()
        
        if existing:
            raise HTTPException(
                status_code=400,
                detail=f"동일한 제품이 이미 등록되어 있습니다: {product_data.manufacturer_part_number}"
            )
        
        # 새 제품 생성
        new_product = TubingProduct(**product_data.dict())
        db.add(new_product)
        db.commit()
        db.refresh(new_product)
        
        # 관련 정보와 함께 조회
        product_with_relations = db.query(TubingProduct)\
            .options(
                joinedload(TubingProduct.specification),
                joinedload(TubingProduct.manufacturer)
            )\
            .filter(TubingProduct.id == new_product.id)\
            .first()
        
        return {
            "id": product_with_relations.id,
            "specification_id": product_with_relations.specification_id,
            "manufacturer_id": product_with_relations.manufacturer_id,
            "manufacturer_part_number": product_with_relations.manufacturer_part_number,
            "manufacturer_product_name": product_with_relations.manufacturer_product_name,
            "spec_name": product_with_relations.specification.spec_name if product_with_relations.specification else None,
            "manufacturer_name": product_with_relations.manufacturer.manufacturer_name if product_with_relations.manufacturer else None,
            "list_price": float(product_with_relations.list_price) if product_with_relations.list_price else None,
            "currency": product_with_relations.currency,
            "lead_time_days": product_with_relations.lead_time_days,
            "availability_status": product_with_relations.availability_status,
            "datasheet_url": product_with_relations.datasheet_url,
            "notes": product_with_relations.notes,
            "is_active": product_with_relations.is_active
        }
        
    except HTTPException:
        db.rollback()
        raise
    except Exception as e:
        db.rollback()
        raise HTTPException(status_code=500, detail=f"제품 등록 실패: {str(e)}")

@router.post("/material-mapping")
async def create_material_tubing_mapping(
    mapping_data: MaterialTubingMappingCreate,
    mapped_by: str = "admin",
    db: Session = Depends(get_db)
):
    """BOM 자재와 Tubing 제품 매핑 생성"""
    try:
        # 기존 매핑 확인
        existing = db.query(MaterialTubingMapping)\
            .filter(
                MaterialTubingMapping.material_id == mapping_data.material_id,
                MaterialTubingMapping.tubing_product_id == mapping_data.tubing_product_id
            ).first()
        
        if existing:
            raise HTTPException(
                status_code=400,
                detail="이미 매핑된 자재와 제품입니다"
            )
        
        # 새 매핑 생성
        new_mapping = MaterialTubingMapping(
            **mapping_data.dict(),
            mapped_by=mapped_by
        )
        db.add(new_mapping)
        db.commit()
        db.refresh(new_mapping)
        
        return {
            "success": True,
            "message": "매핑이 성공적으로 생성되었습니다",
            "mapping_id": new_mapping.id
        }
        
    except HTTPException:
        db.rollback()
        raise
    except Exception as e:
        db.rollback()
        raise HTTPException(status_code=500, detail=f"매핑 생성 실패: {str(e)}")

@router.get("/material-mappings/{material_id}")
async def get_material_tubing_mappings(
    material_id: int,
    db: Session = Depends(get_db)
):
    """특정 자재의 Tubing 매핑 조회"""
    try:
        mappings = db.query(MaterialTubingMapping)\
            .options(
                joinedload(MaterialTubingMapping.tubing_product)
                .joinedload(TubingProduct.specification),
                joinedload(MaterialTubingMapping.tubing_product)
                .joinedload(TubingProduct.manufacturer)
            )\
            .filter(MaterialTubingMapping.material_id == material_id)\
            .all()
        
        result = []
        for mapping in mappings:
            product = mapping.tubing_product
            mapping_dict = {
                "mapping_id": mapping.id,
                "confidence_score": float(mapping.confidence_score) if mapping.confidence_score else None,
                "mapping_method": mapping.mapping_method,
                "mapped_by": mapping.mapped_by,
                "mapped_at": mapping.mapped_at,
                "required_length_m": float(mapping.required_length_m) if mapping.required_length_m else None,
                "calculated_quantity": float(mapping.calculated_quantity) if mapping.calculated_quantity else None,
                "is_verified": mapping.is_verified,
                "tubing_product": {
                    "id": product.id,
                    "manufacturer_part_number": product.manufacturer_part_number,
                    "manufacturer_product_name": product.manufacturer_product_name,
                    "spec_name": product.specification.spec_name if product.specification else None,
                    "manufacturer_name": product.manufacturer.manufacturer_name if product.manufacturer else None,
                    "list_price": float(product.list_price) if product.list_price else None,
                    "currency": product.currency,
                    "availability_status": product.availability_status
                }
            }
            result.append(mapping_dict)
        
        return {
            "success": True,
            "material_id": material_id,
            "mappings": result
        }
        
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"매핑 조회 실패: {str(e)}")

@router.get("/search")
async def search_tubing_products(
    query: str = Query(..., min_length=2),
    category: Optional[str] = Query(None),
    manufacturer: Optional[str] = Query(None),
    min_diameter: Optional[float] = Query(None),
    max_diameter: Optional[float] = Query(None),
    material_grade: Optional[str] = Query(None),
    limit: int = Query(20, ge=1, le=100),
    db: Session = Depends(get_db)
):
    """통합 Tubing 검색 (규격, 제품, 제조사)"""
    try:
        # SQL 쿼리로 복합 검색
        sql_query = """
        SELECT DISTINCT
            tp.id as product_id,
            tp.manufacturer_part_number,
            tp.manufacturer_product_name,
            tp.list_price,
            tp.currency,
            tp.availability_status,
            ts.spec_code,
            ts.spec_name,
            ts.outer_diameter_mm,
            ts.wall_thickness_mm,
            ts.material_grade,
            tc.category_name,
            tm.manufacturer_name,
            tm.country
        FROM tubing_products tp
        JOIN tubing_specifications ts ON tp.specification_id = ts.id
        JOIN tubing_categories tc ON ts.category_id = tc.id
        JOIN tubing_manufacturers tm ON tp.manufacturer_id = tm.id
        WHERE tp.is_active = true 
          AND ts.is_active = true 
          AND tc.is_active = true 
          AND tm.is_active = true
          AND (
              tp.manufacturer_part_number ILIKE :query OR
              tp.manufacturer_product_name ILIKE :query OR
              ts.spec_name ILIKE :query OR
              ts.spec_code ILIKE :query OR
              ts.material_grade ILIKE :query OR
              tm.manufacturer_name ILIKE :query
          )
        """
        
        params = {"query": f"%{query}%"}
        
        # 필터 조건 추가
        if category:
            sql_query += " AND tc.category_code = :category"
            params["category"] = category
            
        if manufacturer:
            sql_query += " AND tm.manufacturer_code = :manufacturer"
            params["manufacturer"] = manufacturer
            
        if min_diameter:
            sql_query += " AND ts.outer_diameter_mm >= :min_diameter"
            params["min_diameter"] = min_diameter
            
        if max_diameter:
            sql_query += " AND ts.outer_diameter_mm <= :max_diameter"
            params["max_diameter"] = max_diameter
            
        if material_grade:
            sql_query += " AND ts.material_grade ILIKE :material_grade"
            params["material_grade"] = f"%{material_grade}%"
        
        sql_query += " ORDER BY tp.manufacturer_part_number LIMIT :limit"
        params["limit"] = limit
        
        result = db.execute(text(sql_query), params)
        products = result.fetchall()
        
        search_results = []
        for product in products:
            product_dict = {
                "product_id": product.product_id,
                "manufacturer_part_number": product.manufacturer_part_number,
                "manufacturer_product_name": product.manufacturer_product_name,
                "list_price": float(product.list_price) if product.list_price else None,
                "currency": product.currency,
                "availability_status": product.availability_status,
                "spec_code": product.spec_code,
                "spec_name": product.spec_name,
                "outer_diameter_mm": float(product.outer_diameter_mm) if product.outer_diameter_mm else None,
                "wall_thickness_mm": float(product.wall_thickness_mm) if product.wall_thickness_mm else None,
                "material_grade": product.material_grade,
                "category_name": product.category_name,
                "manufacturer_name": product.manufacturer_name,
                "country": product.country
            }
            search_results.append(product_dict)
        
        return {
            "success": True,
            "query": query,
            "total_results": len(search_results),
            "results": search_results
        }
        
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"검색 실패: {str(e)}")