from sqlalchemy import Column, Integer, String, Boolean, Float, DateTime, ForeignKey, Text
from sqlalchemy.orm import relationship
from datetime import datetime, timezone
from database import Base


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


class Order(Base):
    __tablename__ = "orders"

    id = Column(Integer, primary_key=True, index=True)
    table_id = Column(Integer, ForeignKey("tables.id"), nullable=True)   # nullable for online orders
    opened_by = Column(Integer, ForeignKey("users.id"), nullable=False)
    opened_at = Column(DateTime(timezone=True), default=_utcnow)
    status = Column(String, default="open", nullable=False)  # open|partially_paid|paid|closed|cancelled
    closed_at = Column(DateTime, nullable=True)
    closed_by = Column(Integer, ForeignKey("users.id"), nullable=True)
    notes = Column(Text, nullable=True)
    business_day_id = Column(Integer, ForeignKey("business_days.id"), nullable=True)
    # KDS
    kds_status = Column(String, default="pending", nullable=False)  # pending|preparing|done
    kds_status_changed_at = Column(DateTime(timezone=True), nullable=True)
    order_type = Column(String, default="here", nullable=False)     # here|takeaway|delivery

    # Xenia Connect — online order fields
    source = Column(String, default="pos", nullable=False)          # "pos" | "online"
    online_order_ref = Column(String, nullable=True)                # e.g. "ORD-0042"
    online_order_cloud_id = Column(Integer, nullable=True)          # cloud DB pk for status mirroring
    online_status = Column(String, nullable=True)                   # mirrors cloud status
    online_customer_name = Column(String, nullable=True)
    online_customer_phone = Column(String, nullable=True)
    online_customer_address = Column(Text, nullable=True)
    online_customer_notes = Column(Text, nullable=True)
    online_order_type = Column(String, nullable=True)               # "delivery" | "dine_in"
    # Phase 2F — customer CRM
    customer_id = Column(Integer, ForeignKey("customers.id"), nullable=True)

    # Number of customers at the table for this order
    customer_count = Column(Integer, nullable=True)

    # Fiscal printer status: NULL = fiscal was off, "pending" | "success" | "failed"
    fiscal_status = Column(String, nullable=True)

    table = relationship("Table", back_populates="orders")
    opener = relationship("User", foreign_keys=[opened_by], back_populates="orders_opened")
    closer = relationship("User", foreign_keys=[closed_by], back_populates="orders_closed")
    customer = relationship("Customer", back_populates="orders", foreign_keys=[customer_id])
    items = relationship("OrderItem", back_populates="order", cascade="all, delete-orphan")
    waiters = relationship("OrderWaiter", back_populates="order", cascade="all, delete-orphan")
    print_logs = relationship("PrintLog", back_populates="order", cascade="all, delete-orphan")
    print_jobs = relationship("PrintJob", back_populates="order", cascade="all, delete-orphan")
    audit_logs = relationship("OrderAuditLog", back_populates="order", cascade="all, delete-orphan")
    discounts = relationship("OrderDiscount", back_populates="order", cascade="all, delete-orphan")


class OrderWaiter(Base):
    __tablename__ = "order_waiters"

    id = Column(Integer, primary_key=True, index=True)
    order_id = Column(Integer, ForeignKey("orders.id"), nullable=False)
    waiter_id = Column(Integer, ForeignKey("users.id"), nullable=False)
    assigned_at = Column(DateTime(timezone=True), default=_utcnow)

    order = relationship("Order", back_populates="waiters")
    waiter = relationship("User", back_populates="order_assignments")


class OrderItem(Base):
    __tablename__ = "order_items"

    id = Column(Integer, primary_key=True, index=True)
    order_id = Column(Integer, ForeignKey("orders.id"), nullable=False)
    product_id = Column(Integer, ForeignKey("products.id"), nullable=False)
    added_by = Column(Integer, ForeignKey("users.id"), nullable=False)
    quantity = Column(Float, nullable=False)
    unit_price = Column(Float, nullable=False)  # price snapshot at time of order
    selected_options = Column(Text, nullable=True)    # JSON array of option ids
    removed_ingredients = Column(Text, nullable=True) # JSON array of ingredient ids
    notes = Column(Text, nullable=True)
    status = Column(String, default="active", nullable=False)   # active|paid|cancelled
    kds_status = Column(String, default="pending", nullable=False)  # pending|preparing|done|served|declined
    added_at = Column(DateTime(timezone=True), default=_utcnow)
    printed = Column(Boolean, default=False, nullable=False)

    # Payment tracking
    paid_by = Column(Integer, ForeignKey("users.id"), nullable=True)
    paid_at = Column(DateTime, nullable=True)
    payment_method = Column(String, nullable=True)  # 'cash'|'card'|'other' — future use
    paid_in_shift_id = Column(Integer, ForeignKey("waiter_shifts.id"), nullable=True)

    # Phase 2A — cost snapshot (copied from product at time of order, never updated)
    unit_cost = Column(Float, nullable=True)

    # On-the-fly price adjustment (positive = surcharge, negative = discount)
    price_adjustment = Column(Float, nullable=False, default=0.0)

    # Phase 2B — cancellation tracking
    cancelled_by = Column(Integer, ForeignKey("users.id"), nullable=True)
    cancelled_at = Column(DateTime(timezone=True), nullable=True)
    cancel_reason = Column(Text, nullable=True)

    # KDS decline tracking
    decline_note = Column(Text, nullable=True)  # reason set by kitchen when declining

    # Courses — optional course assignment for sequenced firing
    course_id = Column(Integer, nullable=True)

    # Deal binding — which deal created this item, and which original item it's linked to
    deal_id = Column(Integer, ForeignKey("deals.id"), nullable=True)
    linked_item_id = Column(Integer, ForeignKey("order_items.id"), nullable=True)

    order = relationship("Order", back_populates="items")
    product = relationship("Product", back_populates="order_items")
    added_by_user = relationship("User", foreign_keys=[added_by], back_populates="order_items")
    paid_by_user = relationship("User", foreign_keys=[paid_by], back_populates="items_paid")

    @property
    def unit_type(self) -> str:
        return self.product.unit_type if self.product and self.product.unit_type else "piece"


class PrintLog(Base):
    __tablename__ = "print_log"

    id = Column(Integer, primary_key=True, index=True)
    order_id = Column(Integer, ForeignKey("orders.id"), nullable=False)
    printer_id = Column(Integer, ForeignKey("printers.id"), nullable=False)
    printed_at = Column(DateTime(timezone=True), default=_utcnow)
    item_ids = Column(Text, nullable=False)  # JSON array of order_item ids
    success = Column(Boolean, nullable=False)
    error_message = Column(Text, nullable=True)

    order = relationship("Order", back_populates="print_logs")
    printer = relationship("Printer", back_populates="print_logs")


class PrintJob(Base):
    """
    One row per (order, printer, zone) print job. Tracks the full lifecycle:
    pending → success or cancelled. PrintLog rows record each individual attempt.
    """
    __tablename__ = "print_jobs"

    id = Column(Integer, primary_key=True, index=True)
    order_id = Column(Integer, ForeignKey("orders.id"), nullable=False)
    printer_id = Column(Integer, ForeignKey("printers.id"), nullable=False)
    zone_id = Column(Integer, nullable=True)            # prep zone id, NULL for legacy
    item_ids = Column(Text, nullable=False)             # JSON array of order_item ids
    copies = Column(Integer, nullable=False, default=1)

    status = Column(String, nullable=False, default="pending")  # pending|success|cancelled
    retry_count = Column(Integer, nullable=False, default=0)
    first_attempted_at = Column(DateTime(timezone=True), default=_utcnow)
    last_attempted_at = Column(DateTime(timezone=True), nullable=True)
    succeeded_at = Column(DateTime(timezone=True), nullable=True)
    cancelled_at = Column(DateTime(timezone=True), nullable=True)
    cancel_reason = Column(String, nullable=True)       # "staff_cancelled" | "order_closed"

    order = relationship("Order", back_populates="print_jobs")
    printer = relationship("Printer", back_populates="print_jobs")


class OrderAuditLog(Base):
    """Immutable append-only audit trail for every action on an order."""
    __tablename__ = "order_audit_log"

    id = Column(Integer, primary_key=True, index=True)
    order_id = Column(Integer, ForeignKey("orders.id"), nullable=False)
    event_type = Column(String, nullable=False)
    # ORDER_OPENED | ITEMS_ADDED | PAYMENT | PAYMENT_OFFLINE | ORDER_CLOSED | ORDER_CANCELLED | ITEM_CANCELLED
    waiter_id = Column(Integer, ForeignKey("users.id"), nullable=True)
    item_ids = Column(Text, nullable=True)      # JSON list of OrderItem ids
    amount = Column(Float, nullable=True)       # total value for PAYMENT events
    payment_method = Column(String, nullable=True)
    note = Column(Text, nullable=True)
    created_at = Column(DateTime(timezone=True), default=_utcnow)
    # Emergency offline payment fields
    offline_uuid = Column(String, nullable=True)   # client-generated UUID for dedup
    offline_at = Column(String, nullable=True)     # ISO timestamp from client
    is_duplicate = Column(Integer, nullable=False, default=0)  # 1 = duplicate payment flagged

    order = relationship("Order", back_populates="audit_logs")
    waiter = relationship("User")

    @property
    def waiter_name(self):
        return self.waiter.username if self.waiter else None


class OrderDiscount(Base):
    """Records a discount applied to an order or a specific item."""
    __tablename__ = "order_discounts"

    id = Column(Integer, primary_key=True, index=True)
    order_id = Column(Integer, ForeignKey("orders.id"), nullable=False)
    item_id = Column(Integer, ForeignKey("order_items.id"), nullable=True)  # NULL = whole-order discount
    discount_type = Column(String, nullable=False)   # 'percent' | 'fixed'
    discount_value = Column(Float, nullable=False)   # e.g. 10.0 = 10% or €10.00
    applied_by = Column(Integer, ForeignKey("users.id"), nullable=False)
    applied_at = Column(DateTime(timezone=True), default=_utcnow)
    reason = Column(Text, nullable=True)

    order = relationship("Order", back_populates="discounts")
    item = relationship("OrderItem")
    applied_by_user = relationship("User")
