import json
from datetime import datetime, timezone
from typing import List, Optional

from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session

from database import get_db
from models.pricing import (
    PriceGroup, PriceModifier, PriceModifierCondition, PriceModifierTarget,
    Deal, DealCondition, DealTarget,
    PriceEventLog, WaiterDiscountSettings,
)
from schemas.pricing import (
    PriceGroupCreate, PriceGroupUpdate, PriceGroupOut,
    PriceModifierCreate, PriceModifierUpdate, PriceModifierOut,
    PriceModifierReorderRequest,
    DealCreate, DealUpdate, DealOut,
    WaiterDiscountSettingsIn, WaiterDiscountSettingsOut,
    PriceEventOut,
)
from routers.deps import get_current_user, require_manager
from models.user import User

router = APIRouter()


def _utcnow():
    return datetime.now(timezone.utc)


# =============================================================================
# PRICE GROUPS
# =============================================================================

@router.get("/groups", response_model=List[PriceGroupOut])
def list_price_groups(
    db: Session = Depends(get_db),
    user: User = Depends(get_current_user),
):
    return db.query(PriceGroup).order_by(PriceGroup.name).all()


@router.post("/groups", response_model=PriceGroupOut, status_code=status.HTTP_201_CREATED)
def create_price_group(
    body: PriceGroupCreate,
    db: Session = Depends(get_db),
    user: User = Depends(require_manager),
):
    pg = PriceGroup(
        name=body.name,
        description=body.description,
        color=body.color,
        is_active=int(body.is_active),
        auto_enable_time=body.auto_enable_time,
        auto_disable_time=body.auto_disable_time,
        auto_days=json.dumps(body.auto_days) if body.auto_days is not None else None,
        created_by=user.id,
    )
    db.add(pg)
    db.commit()
    db.refresh(pg)
    return pg


@router.put("/groups/{group_id}", response_model=PriceGroupOut)
def update_price_group(
    group_id: int,
    body: PriceGroupUpdate,
    db: Session = Depends(get_db),
    user: User = Depends(require_manager),
):
    pg = db.query(PriceGroup).filter(PriceGroup.id == group_id).first()
    if not pg:
        raise HTTPException(status_code=404, detail="Price group not found")
    pg.name = body.name
    pg.description = body.description
    pg.color = body.color
    pg.is_active = int(body.is_active)
    pg.auto_enable_time = body.auto_enable_time
    pg.auto_disable_time = body.auto_disable_time
    pg.auto_days = json.dumps(body.auto_days) if body.auto_days is not None else None
    db.commit()
    db.refresh(pg)
    return pg


@router.patch("/groups/{group_id}/toggle", response_model=PriceGroupOut)
def toggle_price_group(
    group_id: int,
    db: Session = Depends(get_db),
    user: User = Depends(require_manager),
):
    pg = db.query(PriceGroup).filter(PriceGroup.id == group_id).first()
    if not pg:
        raise HTTPException(status_code=404, detail="Price group not found")
    pg.is_active = 0 if pg.is_active else 1
    db.commit()
    db.refresh(pg)
    return pg


@router.delete("/groups/{group_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_price_group(
    group_id: int,
    db: Session = Depends(get_db),
    user: User = Depends(require_manager),
):
    pg = db.query(PriceGroup).filter(PriceGroup.id == group_id).first()
    if not pg:
        raise HTTPException(status_code=404, detail="Price group not found")
    db.delete(pg)
    db.commit()


# =============================================================================
# PRICE MODIFIERS
# =============================================================================

@router.get("/modifiers", response_model=List[PriceModifierOut])
def list_modifiers(
    scope: Optional[str] = None,
    item_id: Optional[int] = None,
    db: Session = Depends(get_db),
    user: User = Depends(get_current_user),
):
    q = db.query(PriceModifier)
    if scope:
        q = q.filter(PriceModifier.scope == scope)
    if item_id is not None:
        q = q.filter(PriceModifier.item_id == item_id)
    return q.order_by(PriceModifier.sort_order).all()


@router.get("/modifiers/favorites", response_model=List[PriceModifierOut])
def list_favorite_modifiers(
    db: Session = Depends(get_db),
    user: User = Depends(get_current_user),
):
    """Returns modifiers flagged as favorites for the dashboard quick-toggle panel."""
    return (
        db.query(PriceModifier)
        .filter(PriceModifier.is_favorite == 1)
        .order_by(PriceModifier.sort_order)
        .all()
    )


@router.post("/modifiers", response_model=PriceModifierOut, status_code=status.HTTP_201_CREATED)
def create_modifier(
    body: PriceModifierCreate,
    db: Session = Depends(get_db),
    user: User = Depends(require_manager),
):
    m = PriceModifier(
        name=body.name,
        description=body.description,
        color=body.color,
        is_active=int(body.is_active),
        is_favorite=int(body.is_favorite),
        allow_stack=int(body.allow_stack),
        sort_order=body.sort_order,
        scope=body.scope,
        item_id=body.item_id,
        action_type=body.action_type,
        action_value=body.action_value,
        round_to=body.round_to,
        created_by=user.id,
    )
    db.add(m)
    db.flush()
    _sync_conditions(m, body.conditions, db, model="modifier")
    _sync_modifier_targets(m, body.targets, db)
    db.commit()
    db.refresh(m)
    return m


@router.put("/modifiers/{modifier_id}", response_model=PriceModifierOut)
def update_modifier(
    modifier_id: int,
    body: PriceModifierUpdate,
    db: Session = Depends(get_db),
    user: User = Depends(require_manager),
):
    m = db.query(PriceModifier).filter(PriceModifier.id == modifier_id).first()
    if not m:
        raise HTTPException(status_code=404, detail="Modifier not found")
    m.name = body.name
    m.description = body.description
    m.color = body.color
    m.is_active = int(body.is_active)
    m.is_favorite = int(body.is_favorite)
    m.allow_stack = int(body.allow_stack)
    m.sort_order = body.sort_order
    m.scope = body.scope
    m.item_id = body.item_id
    m.action_type = body.action_type
    m.action_value = body.action_value
    m.round_to = body.round_to
    m.updated_at = _utcnow()
    m.updated_by = user.id
    # Replace conditions and targets
    for c in list(m.conditions):
        db.delete(c)
    for t in list(m.targets):
        db.delete(t)
    db.flush()
    _sync_conditions(m, body.conditions, db, model="modifier")
    _sync_modifier_targets(m, body.targets, db)
    db.commit()
    db.refresh(m)
    return m


@router.patch("/modifiers/{modifier_id}/toggle", response_model=PriceModifierOut)
def toggle_modifier(
    modifier_id: int,
    db: Session = Depends(get_db),
    user: User = Depends(require_manager),
):
    m = db.query(PriceModifier).filter(PriceModifier.id == modifier_id).first()
    if not m:
        raise HTTPException(status_code=404, detail="Modifier not found")
    m.is_active = 0 if m.is_active else 1
    m.updated_at = _utcnow()
    m.updated_by = user.id
    db.commit()
    db.refresh(m)
    return m


@router.patch("/modifiers/reorder")
def reorder_modifiers(
    body: PriceModifierReorderRequest,
    db: Session = Depends(get_db),
    user: User = Depends(require_manager),
):
    for item in body.items:
        db.query(PriceModifier).filter(PriceModifier.id == item.id).update(
            {"sort_order": item.sort_order}
        )
    db.commit()
    return {"ok": True}


@router.delete("/modifiers/{modifier_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_modifier(
    modifier_id: int,
    db: Session = Depends(get_db),
    user: User = Depends(require_manager),
):
    m = db.query(PriceModifier).filter(PriceModifier.id == modifier_id).first()
    if not m:
        raise HTTPException(status_code=404, detail="Modifier not found")
    db.delete(m)
    db.commit()


# =============================================================================
# DEALS
# =============================================================================

@router.get("/deals", response_model=List[DealOut])
def list_deals(
    db: Session = Depends(get_db),
    user: User = Depends(get_current_user),
):
    return db.query(Deal).order_by(Deal.sort_order).all()


@router.post("/deals", response_model=DealOut, status_code=status.HTTP_201_CREATED)
def create_deal(
    body: DealCreate,
    db: Session = Depends(get_db),
    user: User = Depends(require_manager),
):
    d = Deal(
        name=body.name,
        description=body.description,
        color=body.color,
        is_active=int(body.is_active),
        sort_order=body.sort_order,
        action_type=body.action_type,
        action_modifier_id=body.action_modifier_id,
        action_value=body.action_value,
        action_free_item_id=body.action_free_item_id,
        action_free_target_type=body.action_free_target_type,
        action_free_target_ids=(
            json.dumps(body.action_free_target_ids)
            if body.action_free_target_ids is not None else None
        ),
        action_free_quantity=body.action_free_quantity,
        created_by=user.id,
    )
    db.add(d)
    db.flush()
    _sync_conditions(d, body.conditions, db, model="deal")
    _sync_deal_targets(d, body.targets, db)
    db.commit()
    db.refresh(d)
    return d


@router.put("/deals/{deal_id}", response_model=DealOut)
def update_deal(
    deal_id: int,
    body: DealUpdate,
    db: Session = Depends(get_db),
    user: User = Depends(require_manager),
):
    d = db.query(Deal).filter(Deal.id == deal_id).first()
    if not d:
        raise HTTPException(status_code=404, detail="Deal not found")
    d.name = body.name
    d.description = body.description
    d.color = body.color
    d.is_active = int(body.is_active)
    d.sort_order = body.sort_order
    d.action_type = body.action_type
    d.action_modifier_id = body.action_modifier_id
    d.action_value = body.action_value
    d.action_free_item_id = body.action_free_item_id
    d.action_free_target_type = body.action_free_target_type
    d.action_free_target_ids = (
        json.dumps(body.action_free_target_ids)
        if body.action_free_target_ids is not None else None
    )
    d.action_free_quantity = body.action_free_quantity
    for c in list(d.conditions):
        db.delete(c)
    for t in list(d.targets):
        db.delete(t)
    db.flush()
    _sync_conditions(d, body.conditions, db, model="deal")
    _sync_deal_targets(d, body.targets, db)
    db.commit()
    db.refresh(d)
    return d


@router.patch("/deals/{deal_id}/toggle", response_model=DealOut)
def toggle_deal(
    deal_id: int,
    db: Session = Depends(get_db),
    user: User = Depends(require_manager),
):
    d = db.query(Deal).filter(Deal.id == deal_id).first()
    if not d:
        raise HTTPException(status_code=404, detail="Deal not found")
    d.is_active = 0 if d.is_active else 1
    db.commit()
    db.refresh(d)
    return d


@router.delete("/deals/{deal_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_deal(
    deal_id: int,
    db: Session = Depends(get_db),
    user: User = Depends(require_manager),
):
    d = db.query(Deal).filter(Deal.id == deal_id).first()
    if not d:
        raise HTTPException(status_code=404, detail="Deal not found")
    db.delete(d)
    db.commit()


# =============================================================================
# WAITER DISCOUNT SETTINGS
# =============================================================================

@router.get("/discount-settings/global")
def get_global_discount_settings(
    db: Session = Depends(get_db),
    user: User = Depends(require_manager),
):
    from models.settings import PosSettings
    keys = [
        "discounts.enabled",
        "discounts.max_total_value_workday",
        "discounts.max_total_value_shift",
        "discounts.max_items_per_shift",
    ]
    rows = db.query(PosSettings).filter(PosSettings.key.in_(keys)).all()
    row_map = {r.key.split(".", 1)[1]: r.value for r in rows}
    # Coerce types so frontend toggle/number fields work correctly
    def _coerce(k, v):
        if v is None:
            return None
        if k == "enabled":
            return v.lower() in ("true", "1", "yes")
        try:
            return float(v) if "." in str(v) else int(v)
        except Exception:
            return v
    return {k: _coerce(k, v) for k, v in row_map.items()}


@router.put("/discount-settings/global")
def set_global_discount_settings(
    body: dict,
    db: Session = Depends(get_db),
    user: User = Depends(require_manager),
):
    from models.settings import PosSettings
    allowed = {
        "enabled", "max_total_value_workday",
        "max_total_value_shift", "max_items_per_shift",
    }
    now = _utcnow().isoformat()
    for k, v in body.items():
        if k not in allowed:
            continue
        full_key = f"discounts.{k}"
        row = db.query(PosSettings).filter(PosSettings.key == full_key).first()
        if row:
            row.value = str(v)
            row.updated_at = now
        else:
            db.add(PosSettings(key=full_key, value=str(v), updated_at=now))
    db.commit()
    return {"ok": True}


@router.get("/discount-settings/me", response_model=WaiterDiscountSettingsOut)
def get_my_discount_settings(
    db: Session = Depends(get_db),
    user: User = Depends(get_current_user),
):
    s = db.query(WaiterDiscountSettings).filter(
        WaiterDiscountSettings.user_id == user.id
    ).first()
    if not s:
        return WaiterDiscountSettingsOut(id=0, user_id=user.id, can_apply_discounts=False)
    return s


@router.get("/discount-settings/{user_id}", response_model=WaiterDiscountSettingsOut)
def get_waiter_discount_settings(
    user_id: int,
    db: Session = Depends(get_db),
    user: User = Depends(require_manager),
):
    s = db.query(WaiterDiscountSettings).filter(
        WaiterDiscountSettings.user_id == user_id
    ).first()
    if not s:
        # Return defaults (all None = no limits, disabled)
        return WaiterDiscountSettingsOut(id=0, user_id=user_id, can_apply_discounts=False)
    return s


@router.put("/discount-settings/{user_id}", response_model=WaiterDiscountSettingsOut)
def set_waiter_discount_settings(
    user_id: int,
    body: WaiterDiscountSettingsIn,
    db: Session = Depends(get_db),
    user: User = Depends(require_manager),
):
    s = db.query(WaiterDiscountSettings).filter(
        WaiterDiscountSettings.user_id == user_id
    ).first()
    if not s:
        s = WaiterDiscountSettings(user_id=user_id)
        db.add(s)
    s.can_apply_discounts     = int(body.can_apply_discounts)
    s.max_discount_percent    = body.max_discount_percent
    s.max_discount_amount     = body.max_discount_amount
    s.max_total_value_shift   = body.max_total_value_shift
    s.max_total_value_workday = body.max_total_value_workday
    s.max_items_per_shift     = body.max_items_per_shift
    s.max_items_per_workday   = body.max_items_per_workday
    s.max_items_per_order     = body.max_items_per_order
    db.commit()
    db.refresh(s)
    return s


# =============================================================================
# PRICE EVENT LOG (read-only, for order detail and reports)
# =============================================================================

@router.get("/events/order/{order_id}", response_model=List[PriceEventOut])
def get_order_price_events(
    order_id: int,
    db: Session = Depends(get_db),
    user: User = Depends(get_current_user),
):
    events = (
        db.query(PriceEventLog)
        .filter(PriceEventLog.order_id == order_id)
        .order_by(PriceEventLog.applied_at)
        .all()
    )
    result = []
    for ev in events:
        out = PriceEventOut.model_validate(ev)
        if ev.modifier:
            out.modifier_name = ev.modifier.name
        if ev.deal:
            out.deal_name = ev.deal.name
        if ev.applied_by:
            out.applied_by_username = ev.applied_by.username
        result.append(out)
    return result


# =============================================================================
# DEAL OFFER ACCEPT / DISMISS (PWA-facing)
# =============================================================================

@router.post("/deals/accept")
def accept_deal_offer(
    body: dict,
    db: Session = Depends(get_db),
    user: User = Depends(get_current_user),
):
    """
    Waiter confirms a deal offer. Logs the acceptance and directly adds free item(s)
    to the order with deal_id + linked_item_id set for binding.
    Returns added item IDs so the PWA can refresh.
    """
    from models.pricing import PriceEventLog, Deal
    from models.order import Order, OrderItem
    from models.product import Product

    deal_id = body.get("deal_id")
    order_id = body.get("order_id")
    # For free_choice: list of product_ids the waiter selected + their option snapshots
    # Shape: [{ product_id, quantity, selected_options, notes }] or legacy [int, ...]
    free_items_raw = body.get("free_items", [])
    # Trigger item — the order_item_id that caused the deal to fire (for linking)
    trigger_item_id = body.get("trigger_item_id", None)

    deal = db.query(Deal).filter(Deal.id == deal_id).first()
    if not deal:
        raise HTTPException(status_code=404, detail="Deal not found")

    order = db.query(Order).filter(Order.id == order_id).first()
    if not order:
        raise HTTPException(status_code=404, detail="Order not found")

    added_item_ids = []

    if deal.action_type in ("free_item", "free_choice"):
        # Resolve which products to add
        if deal.action_type == "free_item" and deal.action_free_item_id:
            items_to_add = [{"product_id": deal.action_free_item_id, "quantity": deal.action_free_quantity,
                             "selected_options": None, "notes": None}]
        else:
            # free_choice: caller provides list of chosen products
            items_to_add = []
            for entry in free_items_raw:
                if isinstance(entry, dict):
                    items_to_add.append({
                        "product_id": entry.get("product_id"),
                        "quantity": entry.get("quantity", deal.action_free_quantity),
                        "selected_options": entry.get("selected_options"),
                        "notes": entry.get("notes"),
                    })
                else:
                    items_to_add.append({"product_id": int(entry), "quantity": deal.action_free_quantity,
                                         "selected_options": None, "notes": None})

        for entry in items_to_add:
            product = db.query(Product).filter(Product.id == entry["product_id"]).first()
            if not product:
                continue
            base_price = product.base_price or 0.0
            new_item = OrderItem(
                order_id=order_id,
                product_id=product.id,
                added_by=user.id,
                quantity=entry["quantity"],
                unit_price=base_price,           # full price so the breakdown is readable
                price_adjustment=-base_price,    # negated to bring effective price to 0
                selected_options=json.dumps(entry["selected_options"]) if entry["selected_options"] else None,
                notes=entry.get("notes"),
                deal_id=deal_id,
                linked_item_id=trigger_item_id,
            )
            db.add(new_item)
            db.flush()
            added_item_ids.append(new_item.id)

            db.add(PriceEventLog(
                order_id=order_id,
                order_item_id=new_item.id,
                event_type="free_item_added",
                deal_id=deal_id,
                price_before=product.base_price or 0.0,
                price_after=0.0,
                delta_amount=-(product.base_price or 0.0),
                applied_by_user_id=user.id,
            ))

    # Log acceptance
    db.add(PriceEventLog(
        order_id=order_id,
        order_item_id=trigger_item_id,
        event_type="deal_offer_accepted",
        deal_id=deal_id,
        applied_by_user_id=user.id,
        selected_item_ids=json.dumps([e.get("product_id") if isinstance(e, dict) else int(e) for e in free_items_raw]) if free_items_raw else None,
    ))
    db.commit()

    from services.sse_bus import broadcast_sync
    broadcast_sync()

    return {
        "deal_id": deal_id,
        "action_type": deal.action_type,
        "added_item_ids": added_item_ids,
    }


@router.post("/deals/dismiss")
def dismiss_deal_offer(
    body: dict,
    db: Session = Depends(get_db),
    user: User = Depends(get_current_user),
):
    """Waiter dismisses a deal offer. Logged so it can be re-triggered later."""
    from models.pricing import PriceEventLog
    deal_id = body.get("deal_id")
    order_id = body.get("order_id")

    db.add(PriceEventLog(
        order_id=order_id,
        order_item_id=None,
        event_type="deal_offer_dismissed",
        deal_id=deal_id,
        applied_by_user_id=user.id,
    ))
    db.commit()
    return {"ok": True}


# =============================================================================
# Internal helpers
# =============================================================================

def _sync_conditions(parent, conditions_in, db: Session, model: str):
    """Write condition rows for a modifier or deal."""
    for c in conditions_in:
        if model == "modifier":
            db.add(PriceModifierCondition(
                modifier_id=parent.id,
                condition_type=c.condition_type,
                params=json.dumps(c.params),
            ))
        else:
            db.add(DealCondition(
                deal_id=parent.id,
                condition_type=c.condition_type,
                params=json.dumps(c.params),
            ))


def _sync_modifier_targets(modifier, targets_in, db: Session):
    for t in targets_in:
        db.add(PriceModifierTarget(
            modifier_id=modifier.id,
            target_type=t.target_type,
            target_id=t.target_id,
            target_tag=t.target_tag,
            target_ids=json.dumps(t.target_ids) if t.target_ids else None,
            target_tags=json.dumps(t.target_tags) if t.target_tags else None,
        ))


def _sync_deal_targets(deal, targets_in, db: Session):
    for t in targets_in:
        db.add(DealTarget(
            deal_id=deal.id,
            target_type=t.target_type,
            target_id=t.target_id,
            target_tag=t.target_tag,
            target_ids=json.dumps(t.target_ids) if t.target_ids else None,
            target_tags=json.dumps(t.target_tags) if t.target_tags else None,
        ))
