import uuid
import os
import time
import shutil
from datetime import datetime, timezone, date
from typing import Optional
from fastapi import APIRouter, Depends, HTTPException, Request, Query, UploadFile, File, Body
from sqlalchemy.ext.asyncio import AsyncSession

from app.core.database import get_db
from app.core.security import decode_token
from app.models.dealer_models import DealerPortalLog
from app.modules.dealer_portal import schemas, service
from app.models.sales_models import CustomerMaster, CustomerContact
from app.modules.yard import schemas as yard_schemas, service as yard_service
from app.modules.production_masters import schemas as pm_schemas, service as pm_service

router = APIRouter()


async def get_current_dealer(request: Request, db: AsyncSession = Depends(get_db)) -> dict:
    auth_header = request.headers.get("Authorization")
    if not auth_header or not auth_header.startswith("Bearer "):
        raise HTTPException(status_code=401, detail="Missing or invalid token")

    token = auth_header.split(" ")[1]
    payload = decode_token(token)

    if payload.get("sub_type") != "dealer":
        raise HTTPException(status_code=403, detail="Not authorized as a dealer")

    session_id = payload.get("session_id")
    dealer_id = payload.get("sub")

    session = await db.get(DealerPortalLog, session_id)
    now = datetime.now(timezone.utc)
    sess_exp = session.expires_at if (session and session.expires_at and session.expires_at.tzinfo) else (session.expires_at.replace(tzinfo=timezone.utc) if session and session.expires_at else None)
    if not session or session.invalidated_at is not None or (sess_exp and sess_exp < now):
        raise HTTPException(status_code=401, detail="Session expired or invalid")

    return {"dealer_id": dealer_id, "session_id": uuid.UUID(session_id)}


@router.get("/dashboard")
async def dealer_dashboard(
    start_date: Optional[date] = Query(None),
    end_date: Optional[date] = Query(None),
    dealer: dict = Depends(get_current_dealer),
    db: AsyncSession = Depends(get_db),
):
    return await service.get_dealer_dashboard(db, dealer["dealer_id"], start=start_date, end=end_date)


@router.get("/profile", response_model=schemas.DealerProfileOut)
async def get_profile(dealer: dict = Depends(get_current_dealer), db: AsyncSession = Depends(get_db)):
    from sqlalchemy import select
    from sqlalchemy.orm import selectinload
    result = await db.execute(
        select(CustomerMaster)
        .options(selectinload(CustomerMaster.contacts))
        .where(CustomerMaster.customer_id == dealer["dealer_id"])
    )
    customer = result.scalar_one_or_none()
    if not customer:
        raise HTTPException(status_code=404, detail="Profile not found")
    return customer

@router.put("/profile", response_model=schemas.DealerProfileOut)
async def update_profile(req: schemas.DealerProfileUpdate, dealer: dict = Depends(get_current_dealer), db: AsyncSession = Depends(get_db)):
    from sqlalchemy import select
    from sqlalchemy.orm import selectinload
    result = await db.execute(
        select(CustomerMaster)
        .options(selectinload(CustomerMaster.contacts))
        .where(CustomerMaster.customer_id == dealer["dealer_id"])
    )
    customer = result.scalar_one_or_none()
    if not customer:
        raise HTTPException(status_code=404, detail="Profile not found")
    
    update_data = req.dict(exclude_unset=True)
    for key, value in update_data.items():
        setattr(customer, key, value)
        
    await db.commit()
    await db.refresh(customer)
    return customer

@router.post("/profile/contacts", response_model=schemas.DealerContactOut)
async def create_contact(req: schemas.DealerContactCreate, dealer: dict = Depends(get_current_dealer), db: AsyncSession = Depends(get_db)):
    contact_id = f"CNT-{uuid.uuid4().hex[:6].upper()}"
    new_contact = CustomerContact(
        contact_id=contact_id,
        customer_id=dealer["dealer_id"],
        **req.dict()
    )
    db.add(new_contact)
    await db.commit()
    await db.refresh(new_contact)
    return new_contact

@router.put("/profile/contacts/{contact_id}", response_model=schemas.DealerContactOut)
async def update_contact(contact_id: str, req: schemas.DealerContactUpdate, dealer: dict = Depends(get_current_dealer), db: AsyncSession = Depends(get_db)):
    from sqlalchemy import select
    result = await db.execute(
        select(CustomerContact)
        .where(CustomerContact.contact_id == contact_id, CustomerContact.customer_id == dealer["dealer_id"])
    )
    contact = result.scalar_one_or_none()
    if not contact:
        raise HTTPException(status_code=404, detail="Contact not found")
        
    update_data = req.dict(exclude_unset=True)
    for key, value in update_data.items():
        setattr(contact, key, value)
        
    await db.commit()
    await db.refresh(contact)
    return contact

@router.delete("/profile/contacts/{contact_id}")
async def delete_contact(contact_id: str, dealer: dict = Depends(get_current_dealer), db: AsyncSession = Depends(get_db)):
    from sqlalchemy import select
    result = await db.execute(
        select(CustomerContact)
        .where(CustomerContact.contact_id == contact_id, CustomerContact.customer_id == dealer["dealer_id"])
    )
    contact = result.scalar_one_or_none()
    if not contact:
        raise HTTPException(status_code=404, detail="Contact not found")
        
    await db.delete(contact)
    await db.commit()
    return {"message": "Contact deleted"}

@router.post("/login", response_model=schemas.DealerLoginResponse)
async def login(req: schemas.DealerLoginRequest, request: Request, db: AsyncSession = Depends(get_db)):
    return await service.authenticate_dealer(db, req, request.client.host, request.headers.get("user-agent", ""))


@router.post("/logout")
async def logout(dealer: dict = Depends(get_current_dealer), db: AsyncSession = Depends(get_db)):
    await service.logout_dealer(db, dealer["session_id"])
    return {"message": "Logged out successfully"}


@router.get("/clients", response_model=list[schemas.ClientOut])
async def list_clients(dealer: dict = Depends(get_current_dealer), db: AsyncSession = Depends(get_db)):
    return await service.list_clients(db, dealer["dealer_id"])


@router.get("/clients/search", response_model=list[schemas.ClientSearchResult])
async def search_clients(
    q: str = Query("", description="Partial client name to search for"),
    dealer: dict = Depends(get_current_dealer),
    db: AsyncSession = Depends(get_db),
):
    return await service.search_clients(db, dealer["dealer_id"], q)


@router.get("/clients/{client_id}", response_model=schemas.ClientOut)
async def get_client(client_id: str, dealer: dict = Depends(get_current_dealer), db: AsyncSession = Depends(get_db)):
    return await service.get_client(db, dealer["dealer_id"], client_id)


@router.post("/clients", response_model=schemas.ClientOut, status_code=201)
async def create_client(
    payload: schemas.ClientCreate,
    request: Request,
    dealer: dict = Depends(get_current_dealer),
    db: AsyncSession = Depends(get_db),
):
    client = await service.create_client(db, dealer["dealer_id"], payload, created_by_dealer=True)
    await service.log_dealer_action(
        db, dealer["dealer_id"], "CLIENT_CREATED", entity_type="DealerClientMaster",
        entity_id=client.client_id, ip=request.client.host,
    )
    return client


@router.put("/clients/{client_id}", response_model=schemas.ClientOut)
async def update_client(
    client_id: str,
    payload: schemas.ClientUpdate,
    request: Request,
    dealer: dict = Depends(get_current_dealer),
    db: AsyncSession = Depends(get_db),
):
    client = await service.update_client(db, dealer["dealer_id"], client_id, payload)
    await service.log_dealer_action(
        db, dealer["dealer_id"], "CLIENT_UPDATED", entity_type="DealerClientMaster",
        entity_id=client.client_id, ip=request.client.host,
    )
    return client


@router.get("/order-masters", response_model=schemas.OrderMastersOut)
async def get_order_masters(dealer: dict = Depends(get_current_dealer), db: AsyncSession = Depends(get_db)):
    return await service.get_order_masters(db)


@router.get("/chassis-lookup", response_model=yard_schemas.ChassisLookupOut)
async def chassis_lookup(
    chassis_no: str = Query(..., min_length=1),
    dealer: dict = Depends(get_current_dealer),
    db: AsyncSession = Depends(get_db),
):
    return await yard_service.lookup_chassis(db, chassis_no)


@router.get("/my-chassis", response_model=schemas.DealerChassisListResponse)
async def get_my_chassis(
    client_id: str | None = Query(None),
    yard_only: bool = Query(False),
    dealer: dict = Depends(get_current_dealer),
    db: AsyncSession = Depends(get_db),
):
    return await service.get_dealer_chassis(db, dealer["dealer_id"], client_id=client_id, yard_only=yard_only)


@router.get("/orders", response_model=list[schemas.OrderListItem])
async def list_orders(
    client_id: str | None = Query(None),
    dealer: dict = Depends(get_current_dealer),
    db: AsyncSession = Depends(get_db),
):
    return await service.list_orders(db, dealer["dealer_id"], client_id)


@router.get("/orders/{order_id}", response_model=schemas.OrderDetailOut)
async def get_order_detail(order_id: uuid.UUID, dealer: dict = Depends(get_current_dealer), db: AsyncSession = Depends(get_db)):
    return await service.get_order_detail(db, dealer["dealer_id"], order_id)


@router.post("/orders", response_model=schemas.OrderDetailOut, status_code=201)
async def create_order(
    payload: schemas.OrderCreate,
    request: Request,
    dealer: dict = Depends(get_current_dealer),
    db: AsyncSession = Depends(get_db),
):
    order = await service.create_order(db, dealer["dealer_id"], payload)
    await service.log_dealer_action(
        db, dealer["dealer_id"], "ORDER_SUBMITTED", entity_type="DealerOrder",
        entity_id=str(order.order_id), ip=request.client.host,
    )
    return await service.get_order_detail(db, dealer["dealer_id"], order.order_id)


@router.post("/orders/{order_id}/acknowledge", response_model=schemas.OrderDetailOut)
async def acknowledge_order(
    order_id: uuid.UUID, request: Request,
    dealer: dict = Depends(get_current_dealer), db: AsyncSession = Depends(get_db),
):
    order = await service.acknowledge_order(db, dealer["dealer_id"], order_id)
    await service.log_dealer_action(
        db, dealer["dealer_id"], "ORDER_ACKNOWLEDGED", entity_type="DealerOrder",
        entity_id=str(order_id), ip=request.client.host,
    )
    return order


@router.put("/orders/{order_id}", response_model=schemas.OrderDetailOut)
async def edit_order(
    order_id: uuid.UUID, payload: schemas.OrderEditRequest, request: Request,
    dealer: dict = Depends(get_current_dealer), db: AsyncSession = Depends(get_db),
):
    order = await service.edit_order(db, dealer["dealer_id"], order_id, payload)
    await service.log_dealer_action(
        db, dealer["dealer_id"], "ORDER_EDITED", entity_type="DealerOrder",
        entity_id=str(order_id), ip=request.client.host,
    )
    return order


@router.post("/orders/{order_id}/allocate", response_model=schemas.OrderDetailOut)
async def dealer_allocate_chassis(
    order_id: uuid.UUID,
    allocations: dict = Body(..., embed=True),
    request: Request = None,
    dealer: dict = Depends(get_current_dealer),
    db: AsyncSession = Depends(get_db),
):
    order = await service.allocate_order_chassis(
        db,
        user_id=None,
        order_id=order_id,
        allocations=allocations,
        is_dealer=True,
        dealer_id=dealer["dealer_id"],
    )
    if request:
        await service.log_dealer_action(
            db, dealer["dealer_id"], "CHASSIS_ALLOCATED", entity_type="DealerOrder",
            entity_id=str(order_id), ip=request.client.host,
        )
    return order

@router.post("/requests", response_model=schemas.DealerRequestOut, status_code=201)
async def create_dealer_request(
    request: Request,
    dealer: dict = Depends(get_current_dealer),
    db: AsyncSession = Depends(get_db),
):
    content_type = request.headers.get("content-type", "")
    photo_url = None
    
    if "multipart/form-data" in content_type:
        form = await request.form()
        request_type = form.get("request_type")
        details = form.get("details")
        file_objs = form.getlist("files")
        
        # Backward compatibility for single file inputs
        if form.get("file"):
            file_objs.append(form.get("file"))
            
        if not request_type or not details:
            raise HTTPException(status_code=422, detail="request_type and details are required")
            
        safe_dealer_id = str(dealer.get('dealer_id', 'unknown')).strip().replace(" ", "_").replace("/", "_")
        safe_req_type = str(request_type).strip().replace(" ", "_").replace("/", "_")
        
        # New directory structure: uploads/dealer_requests/{dealer_id}/{request_type}/
        dir_path = os.path.join("uploads", "dealer_requests", safe_dealer_id, safe_req_type)
        if file_objs and any(hasattr(f, "filename") and f.filename for f in file_objs):
            os.makedirs(dir_path, exist_ok=True)
            
        photo_urls = []
        for file_obj in file_objs:
            if hasattr(file_obj, "filename") and file_obj.filename:
                ext = os.path.splitext(file_obj.filename)[1] or ".jpg"
                filename = f"req_{int(time.time() * 1000)}_{uuid.uuid4().hex[:6]}{ext}"
                file_path = os.path.join(dir_path, filename)
                
                with open(file_path, "wb") as buffer:
                    shutil.copyfileobj(file_obj.file, buffer)
                    
                # Web path uses forward slashes
                photo_urls.append(f"/uploads/dealer_requests/{safe_dealer_id}/{safe_req_type}/{filename}")
        
        if photo_urls:
            photo_url = photo_urls[0]
            
    else:
        body = await request.json()
        request_type = body.get("request_type")
        details = body.get("details")
        photo_urls = []
        
        if not request_type or not details:
            raise HTTPException(status_code=422, detail="request_type and details are required")

    new_req = await service.create_dealer_request(db, dealer["dealer_id"], request_type, details, photo_url=photo_url, photo_urls=photo_urls)
    await service.log_dealer_action(
        db, dealer["dealer_id"], "REQUEST_CREATED", entity_type="DealerRequest",
        entity_id=str(new_req.request_id), ip=request.client.host,
    )
    return new_req

@router.get("/requests", response_model=list[schemas.DealerRequestOut])
async def list_dealer_requests(
    dealer: dict = Depends(get_current_dealer),
    db: AsyncSession = Depends(get_db),
):
    return await service.list_dealer_requests(db, dealer["dealer_id"])


@router.get("/requests/{request_id}", response_model=schemas.DealerRequestOut)
async def get_dealer_request(
    request_id: uuid.UUID,
    dealer: dict = Depends(get_current_dealer),
    db: AsyncSession = Depends(get_db),
):
    return await service.get_dealer_request(db, dealer["dealer_id"], request_id)

# ── Templates ──

@router.get("/templates", response_model=list[schemas.VehicleTemplateOut])
async def list_vehicle_templates(
    dealer: dict = Depends(get_current_dealer),
    db: AsyncSession = Depends(get_db),
):
    return await service.list_vehicle_templates(db, dealer["dealer_id"])

@router.post("/templates", response_model=schemas.VehicleTemplateOut, status_code=201)
async def create_vehicle_template(
    payload: schemas.VehicleTemplateCreate,
    db: AsyncSession = Depends(get_db),
    dealer: dict = Depends(get_current_dealer)
):
    return await service.create_vehicle_template(db, dealer["dealer_id"], payload)


@router.put("/templates/{template_id}", response_model=schemas.VehicleTemplateOut)
async def update_vehicle_template(
    template_id: uuid.UUID,
    payload: schemas.VehicleTemplateCreate,
    db: AsyncSession = Depends(get_db),
    dealer: dict = Depends(get_current_dealer)
):
    return await service.update_vehicle_template(db, dealer["dealer_id"], template_id, payload)


@router.delete("/templates/{template_id}")
async def delete_vehicle_template(
    template_id: uuid.UUID,
    db: AsyncSession = Depends(get_db),
    dealer: dict = Depends(get_current_dealer)
):
    return await service.delete_vehicle_template(db, dealer["dealer_id"], template_id)

@router.post("/upload-image")
async def upload_feature_image(
    file: UploadFile = File(...),
    dealer: dict = Depends(get_current_dealer),
):
    import shutil
    import os
    import time
    
    # Use dealer_id if possible
    safe_dealer_id = str(dealer.get('dealer_id', 'unknown')).strip().replace(" ", "_").replace("/", "_")
    dir_path = os.path.join("uploads", "dealer_features", safe_dealer_id)
    os.makedirs(dir_path, exist_ok=True)
    
    ext = file.filename.split('.')[-1] if '.' in file.filename else 'png'
    filename = f"feature_{int(time.time())}.{ext}"
    file_path = os.path.join(dir_path, filename)
    
    with open(file_path, "wb") as buffer:
        shutil.copyfileobj(file.file, buffer)
        
    return {"image_url": f"/uploads/dealer_features/{safe_dealer_id}/{filename}"}


@router.get("/seating-layouts", response_model=list[pm_schemas.SeatingLayoutOut])
async def list_seating_layouts(
    dealer: dict = Depends(get_current_dealer),
    db: AsyncSession = Depends(get_db),
):
    return await pm_service.list_seating_layouts(db)


@router.get("/seating-layouts/search", response_model=list[pm_schemas.SeatingLayoutOut])
async def search_seating_layouts(
    chassis_model: str,
    body_type: str,
    segment: str,
    door_type: str,
    seat_type: str,
    dealer: dict = Depends(get_current_dealer),
    db: AsyncSession = Depends(get_db),
):
    return await pm_service.search_seating_layouts(db, chassis_model, body_type, segment, door_type, seat_type)

