import uuid
from datetime import datetime, timezone
from typing import Optional, List
from sqlalchemy import select, update, func, or_
from sqlalchemy.ext.asyncio import AsyncSession

from app.models.notification_models import Notification
from app.models.core_models import LoginUsers
from app.models.access_models import RoleModulePermission
from app.models.dealer_models import DealerOrder, DealerOrderVehicle
from app.models.sales_models import CustomerMaster
from app.core.logging import app_logger


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


async def get_user_notifications(
    db: AsyncSession,
    user_id: uuid.UUID,
    limit: int = 30,
    unread_only: bool = False
) -> tuple[List[Notification], int]:
    """
    Fetches notifications for an internal staff user, plus the total unread count.
    """
    # Unread count
    count_stmt = (
        select(func.count(Notification.notification_id))
        .where(
            Notification.recipient_type == 'STAFF',
            Notification.user_id == user_id,
            Notification.is_read == False
        )
    )
    unread_count = (await db.execute(count_stmt)).scalar() or 0

    # Query items
    stmt = (
        select(Notification)
        .where(
            Notification.recipient_type == 'STAFF',
            Notification.user_id == user_id
        )
    )
    if unread_only:
        stmt = stmt.where(Notification.is_read == False)

    stmt = stmt.order_by(Notification.created_at.desc()).limit(limit)
    items = (await db.execute(stmt)).scalars().all()
    return list(items), unread_count


async def get_dealer_notifications(
    db: AsyncSession,
    dealer_id: str,
    limit: int = 30,
    unread_only: bool = False
) -> tuple[List[Notification], int]:
    """
    Fetches notifications for a dealer portal account, plus the total unread count.
    """
    count_stmt = (
        select(func.count(Notification.notification_id))
        .where(
            Notification.recipient_type == 'DEALER',
            Notification.dealer_id == dealer_id,
            Notification.is_read == False
        )
    )
    unread_count = (await db.execute(count_stmt)).scalar() or 0

    stmt = (
        select(Notification)
        .where(
            Notification.recipient_type == 'DEALER',
            Notification.dealer_id == dealer_id
        )
    )
    if unread_only:
        stmt = stmt.where(Notification.is_read == False)

    stmt = stmt.order_by(Notification.created_at.desc()).limit(limit)
    items = (await db.execute(stmt)).scalars().all()
    return list(items), unread_count


async def mark_notification_as_read(
    db: AsyncSession,
    notification_id: uuid.UUID,
    user_id: Optional[uuid.UUID] = None,
    dealer_id: Optional[str] = None
) -> bool:
    """
    Marks a single notification as read if it belongs to the requesting recipient.
    """
    notif = await db.get(Notification, notification_id)
    if not notif:
        return False

    if user_id and notif.user_id != user_id:
        return False
    if dealer_id and notif.dealer_id != dealer_id:
        return False

    notif.is_read = True
    notif.read_at = now_utc()
    await db.commit()
    return True


async def mark_all_notifications_as_read(
    db: AsyncSession,
    user_id: Optional[uuid.UUID] = None,
    dealer_id: Optional[str] = None
) -> int:
    """
    Marks all notifications for a recipient as read.
    """
    now = now_utc()
    if user_id:
        stmt = (
            update(Notification)
            .where(
                Notification.recipient_type == 'STAFF',
                Notification.user_id == user_id,
                Notification.is_read == False
            )
            .values(is_read=True, read_at=now)
        )
    elif dealer_id:
        stmt = (
            update(Notification)
            .where(
                Notification.recipient_type == 'DEALER',
                Notification.dealer_id == dealer_id,
                Notification.is_read == False
            )
            .values(is_read=True, read_at=now)
        )
    else:
        return 0

    res = await db.execute(stmt)
    await db.commit()
    return res.rowcount or 0


async def _get_sales_team_user_ids(db: AsyncSession) -> List[uuid.UUID]:
    """
    Resolves the specific staff users responsible for Sales & Dealer Orders.
    Includes users with explicit Sales roles (SALES_MGR, SALES_EXEC, CLIENT_ADMIN, ADMIN)
    or users who have view/edit permission for the SALES module.
    """
    # 1. By role name/code
    direct_roles = ['SALES_MGR', 'SALES_EXEC', 'ADMIN', 'CLIENT_ADMIN']
    users_by_role = (await db.execute(
        select(LoginUsers.user_id).where(
            LoginUsers.is_active == True,
            or_(
                LoginUsers.role_code.in_(direct_roles),
                LoginUsers.role_code.ilike('%SALES%')
            )
        )
    )).scalars().all()

    # 2. By module permission
    users_by_perm = (await db.execute(
        select(LoginUsers.user_id)
        .join(RoleModulePermission, LoginUsers.role_code == RoleModulePermission.role_code)
        .where(
            LoginUsers.is_active == True,
            RoleModulePermission.module_code.in_(['SALES', 'DEALER_PORTAL_ADMIN']),
            RoleModulePermission.can_view == True
        )
    )).scalars().all()

    combined = set(users_by_role) | set(users_by_perm)
    return list(combined)


async def notify_order_submitted(
    db: AsyncSession,
    order: DealerOrder,
    is_resubmission: bool = False
):
    """
    Dispatches notifications when an order is created or submitted:
    1. Sends confirmation notification to the Dealer Portal
    2. Sends actionable notification to the Sales team in the internal ERP
    """
    try:
        # Fetch dealer and vehicle details
        dealer = await db.get(CustomerMaster, order.dealer_id)
        dealer_name = dealer.name if dealer else order.dealer_id

        # Calculate vehicle count
        v_qty = (await db.execute(
            select(func.sum(DealerOrderVehicle.qty))
            .where(DealerOrderVehicle.order_id == order.order_id)
        )).scalar() or 1

        order_no = order.order_no or str(order.order_id)[:8].upper()

        # ── 1. NOTIFY DEALER PORTAL ─────────────────────────
        dealer_title = "Order Submitted Successfully" if not is_resubmission else "Order Changes Resubmitted"
        dealer_msg = (
            f"Your order #{order_no} ({v_qty} vehicle{'s' if v_qty > 1 else ''}) has been received "
            f"and is currently under review by the Sales team."
        ) if not is_resubmission else (
            f"Your updated configuration for order #{order_no} has been resubmitted for Sales review."
        )

        db.add(Notification(
            notification_id=uuid.uuid4(),
            recipient_type='DEALER',
            dealer_id=order.dealer_id,
            category='ORDER',
            event_type='ORDER_SUBMITTED' if not is_resubmission else 'ORDER_RESUBMITTED',
            title=dealer_title,
            message=dealer_msg,
            reference_type='DEALER_ORDER',
            reference_id=str(order.order_id),
            action_url='/dealer-portal/orders',
            is_read=False,
            created_at=now_utc(),
        ))

        # ── 2. NOTIFY SALES TEAM (INTERNAL ERP) ────────────
        sales_user_ids = await _get_sales_team_user_ids(db)
        sales_title = f"New Order: #{order_no}" if not is_resubmission else f"Order Resubmitted: #{order_no}"
        sales_msg = (
            f"Dealer '{dealer_name}' submitted new order #{order_no} for {v_qty} vehicle{'s' if v_qty > 1 else ''}."
        ) if not is_resubmission else (
            f"Dealer '{dealer_name}' resubmitted revised specs for order #{order_no}."
        )

        for uid in sales_user_ids:
            db.add(Notification(
                notification_id=uuid.uuid4(),
                recipient_type='STAFF',
                user_id=uid,
                category='ORDER',
                event_type='ORDER_RECEIVED' if not is_resubmission else 'ORDER_UPDATED',
                title=sales_title,
                message=sales_msg,
                reference_type='DEALER_ORDER',
                reference_id=str(order.order_id),
                action_url=f"/sales/dealer-orders?order_id={order.order_id}",
                is_read=False,
                created_at=now_utc(),
            ))

        await db.commit()
    except Exception as e:
        app_logger.error(f"[NOTIFICATIONS] Failed to dispatch order submitted notification: {e}")


async def notify_order_confirmed_by_sales(
    db: AsyncSession,
    order: DealerOrder,
    admin_id: Optional[uuid.UUID] = None
):
    """
    Dispatches notification to Dealer Portal when Sales reviews and confirms an order (pending dealer ack).
    """
    try:
        order_no = order.order_no or str(order.order_id)[:8].upper()
        db.add(Notification(
            notification_id=uuid.uuid4(),
            recipient_type='DEALER',
            dealer_id=order.dealer_id,
            category='ORDER',
            event_type='SALES_CONFIRMED',
            title="Order Reviewed by Sales",
            message=f"Sales has reviewed order #{order_no}. Please review the assigned specifications and acknowledge.",
            reference_type='DEALER_ORDER',
            reference_id=str(order.order_id),
            action_url='/dealer-portal/orders',
            is_read=False,
            created_at=now_utc(),
        ))
        await db.commit()
    except Exception as e:
        app_logger.error(f"[NOTIFICATIONS] Failed to dispatch order sales confirmed notification: {e}")


async def notify_order_acknowledged_by_dealer(
    db: AsyncSession,
    order: DealerOrder
):
    """
    Dispatches notification to Sales Team when Dealer acknowledges the order.
    """
    try:
        dealer = await db.get(CustomerMaster, order.dealer_id)
        dealer_name = dealer.name if dealer else order.dealer_id
        order_no = order.order_no or str(order.order_id)[:8].upper()

        sales_user_ids = await _get_sales_team_user_ids(db)
        for uid in sales_user_ids:
            db.add(Notification(
                notification_id=uuid.uuid4(),
                recipient_type='STAFF',
                user_id=uid,
                category='ORDER',
                event_type='ORDER_ACKNOWLEDGED',
                title=f"Order Acknowledged: #{order_no}",
                message=f"Dealer '{dealer_name}' has acknowledged order #{order_no}. Ready for final confirmation/production.",
                reference_type='DEALER_ORDER',
                reference_id=str(order.order_id),
                action_url=f"/sales/dealer-orders?order_id={order.order_id}",
                is_read=False,
                created_at=now_utc(),
            ))
        await db.commit()
    except Exception as e:
        app_logger.error(f"[NOTIFICATIONS] Failed to dispatch order acknowledged notification: {e}")


async def notify_order_final_confirmed(
    db: AsyncSession,
    order: DealerOrder
):
    """
    Dispatches notification to Dealer Portal when order is finally confirmed and locked for production.
    """
    try:
        order_no = order.order_no or str(order.order_id)[:8].upper()
        db.add(Notification(
            notification_id=uuid.uuid4(),
            recipient_type='DEALER',
            dealer_id=order.dealer_id,
            category='ORDER',
            event_type='ORDER_LOCKED',
            title="Order Confirmed & Scheduled",
            message=f"Order #{order_no} is officially confirmed and locked into the production pipeline.",
            reference_type='DEALER_ORDER',
            reference_id=str(order.order_id),
            action_url='/dealer-portal/orders',
            is_read=False,
            created_at=now_utc(),
        ))
        await db.commit()
    except Exception as e:
        app_logger.error(f"[NOTIFICATIONS] Failed to dispatch final order confirmed notification: {e}")
