"""Notification helpers: create rows, run alert checks, send email/WhatsApp."""
import smtplib
import logging
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from datetime import datetime, timedelta
from sqlalchemy.orm import Session

from ..models import Notification, NotificationPreference, SystemSetting, User

logger = logging.getLogger(__name__)


def _get_setting(db: Session, key: str, default: str = '') -> str:
    s = db.query(SystemSetting).filter(SystemSetting.setting_key == key).first()
    return s.setting_value if s else default


def create_notification(
    db: Session,
    type: str,
    title: str,
    message: str = None,
    link: str = None,
    target_role: str = 'ALL',
    user_ids: list = None,
):
    """Insert notification row(s) and dispatch emails to eligible users."""
    if user_ids:
        for uid in user_ids:
            n = Notification(
                user_id=uid,
                type=type,
                title=title,
                message=message,
                link=link,
            )
            db.add(n)
        eligible_ids = list(user_ids)
    else:
        n = Notification(
            target_role=target_role,
            type=type,
            title=title,
            message=message,
            link=link,
        )
        db.add(n)
        # resolve which users receive role-broadcast notifications
        q = db.query(User.id)
        if target_role != 'ALL':
            q = q.filter(User.role == target_role)
        eligible_ids = [r.id for r in q.all()]

    try:
        db.commit()
    except Exception:
        db.rollback()
        return

    # Dispatch emails after successful commit
    for uid in eligible_ids:
        try:
            send_email_notification(db, uid, title, message or '')
        except Exception as e:
            logger.warning(f"Email dispatch failed for user {uid}: {e}")


def check_unclosed_shifts(db: Session) -> dict:
    """Find ACTIVE/DRAFT shifts older than unclosed_shift_hours → create notifications."""
    from ..models import DailyShift
    hours = int(_get_setting(db, 'unclosed_shift_hours', '14'))
    cutoff = datetime.utcnow() - timedelta(hours=hours)
    old_shifts = (
        db.query(DailyShift)
        .filter(
            DailyShift.status.in_(['ACTIVE', 'DRAFT']),
            DailyShift.created_at <= cutoff,
        )
        .all()
    )
    count = 0
    for shift in old_shifts:
        # Avoid duplicates: check if we already have an unread notification for this shift
        existing = (
            db.query(Notification)
            .filter(
                Notification.type == 'UNCLOSED_SHIFT',
                Notification.is_read == False,
                Notification.link == f"/history",
            )
            .first()
        )
        if not existing:
            create_notification(
                db,
                type='UNCLOSED_SHIFT',
                title=f"Unclosed Shift — {shift.shift_type} {shift.record_date}",
                message=f"Shift #{shift.id} ({shift.shift_type}) on {shift.record_date} "
                        f"has been open for over {hours} hours.",
                link="/history",
                target_role='OWNER',
            )
            count += 1
    return {"checked": len(old_shifts), "notifications_created": count}


def check_credit_limits(db: Session) -> dict:
    """Find customers where current_balance > credit_limit → create CREDIT_LIMIT notifications."""
    from ..models import CreditCustomer
    over_limit = (
        db.query(CreditCustomer)
        .filter(
            CreditCustomer.status == 'ACTIVE',
            CreditCustomer.current_balance > CreditCustomer.credit_limit,
            CreditCustomer.credit_limit > 0,
        )
        .all()
    )
    count = 0
    for cust in over_limit:
        existing = (
            db.query(Notification)
            .filter(
                Notification.type == 'CREDIT_LIMIT',
                Notification.is_read == False,
                Notification.link == f"/customers/{cust.id}",
            )
            .first()
        )
        if not existing:
            create_notification(
                db,
                type='CREDIT_LIMIT',
                title=f"Credit Limit Exceeded — {cust.company_name}",
                message=(
                    f"Balance Rs {float(cust.current_balance):,.2f} exceeds "
                    f"limit Rs {float(cust.credit_limit):,.2f}."
                ),
                link=f"/customers/{cust.id}",
                target_role='OWNER',
            )
            count += 1
    return {"checked": len(over_limit), "notifications_created": count}


def check_low_stock(db: Session) -> dict:
    """Find latest tank dips below threshold settings → create LOW_STOCK notifications."""
    from ..models import TankDipReading
    fuel_keys = {
        'LP92': 'low_stock_threshold_lp92',
        'EURO3': 'low_stock_threshold_euro3',
        'LAD': 'low_stock_threshold_lad',
        'LADXM': 'low_stock_threshold_ladxm',
    }
    count = 0
    checked = 0
    for fuel, key in fuel_keys.items():
        threshold = float(_get_setting(db, key, '0'))
        if threshold <= 0:
            continue
        latest = (
            db.query(TankDipReading)
            .filter(TankDipReading.tank_name == fuel)
            .order_by(TankDipReading.read_date.desc())
            .first()
        )
        if not latest:
            continue
        checked += 1
        if float(latest.volume_ltr) < threshold:
            existing = (
                db.query(Notification)
                .filter(
                    Notification.type == 'LOW_STOCK',
                    Notification.is_read == False,
                    Notification.link == '/tank-readings',
                )
                .first()
            )
            if not existing:
                create_notification(
                    db,
                    type='LOW_STOCK',
                    title=f"Low Stock Alert — {fuel}",
                    message=(
                        f"{fuel} tank at {float(latest.volume_ltr):,.0f}L "
                        f"(threshold: {threshold:,.0f}L) as of {latest.read_date}."
                    ),
                    link='/tank-readings',
                    target_role='OWNER',
                )
                count += 1
    return {"checked": checked, "notifications_created": count}


def _send_email(db: Session, to_email: str, title: str, message: str) -> None:
    """Core email send — reads SMTP settings from DB. Raises on failure."""
    smtp_host  = _get_setting(db, 'smtp_host')
    smtp_port  = int(_get_setting(db, 'smtp_port', '587'))
    smtp_user  = _get_setting(db, 'smtp_user')
    smtp_pass  = _get_setting(db, 'smtp_password')
    from_email = _get_setting(db, 'smtp_from_email') or smtp_user

    if not smtp_host:
        raise ValueError("SMTP host not configured")

    msg = MIMEMultipart('alternative')
    msg['Subject'] = title
    msg['From']    = from_email
    msg['To']      = to_email
    msg.attach(MIMEText(message, 'plain'))

    # Port 465 → implicit SSL; port 25 → plain (local relay, no TLS); else → STARTTLS
    if smtp_port == 465:
        with smtplib.SMTP_SSL(smtp_host, smtp_port) as server:
            if smtp_user and smtp_pass:
                server.login(smtp_user, smtp_pass)
            server.sendmail(from_email, [to_email], msg.as_string())
    elif smtp_port == 25:
        with smtplib.SMTP(smtp_host, smtp_port) as server:
            server.ehlo()
            if smtp_user and smtp_pass:
                server.login(smtp_user, smtp_pass)
            server.sendmail(from_email, [to_email], msg.as_string())
    else:
        with smtplib.SMTP(smtp_host, smtp_port) as server:
            server.ehlo()
            server.starttls()
            if smtp_user and smtp_pass:
                server.login(smtp_user, smtp_pass)
            server.sendmail(from_email, [to_email], msg.as_string())


def send_email_notification(db: Session, user_id: int, title: str, message: str) -> None:
    """Send email via SMTP settings. Should run as a BackgroundTask."""
    prefs = db.query(NotificationPreference).filter(
        NotificationPreference.user_id == user_id
    ).first()
    if not prefs or not prefs.email_enabled or not prefs.email:
        return
    try:
        _send_email(db, to_email=prefs.email, title=title, message=message)
    except Exception as e:
        logger.warning(f"Email send failed for user {user_id}: {e}")


def send_whatsapp_notification(db: Session, number: str, message: str) -> None:
    """POST to WhatsApp Business API. Should run as a BackgroundTask."""
    import httpx
    api_url   = _get_setting(db, 'whatsapp_api_url')
    api_token = _get_setting(db, 'whatsapp_api_token')
    if not api_url or not api_token:
        return
    try:
        httpx.post(
            api_url,
            json={"to": number, "body": message},
            headers={"Authorization": f"Bearer {api_token}"},
            timeout=10,
        )
    except Exception as e:
        logger.warning(f"WhatsApp send failed to {number}: {e}")
