"""Time Tracking services — business logic."""

from __future__ import annotations

from datetime import date
from decimal import Decimal

from django.db import transaction
from django.utils import timezone


# ---------------------------------------------------------------------------
# Constants
# ---------------------------------------------------------------------------

DEFAULT_MAX_HOURS_PER_DAY = Decimal("24")


# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------

def _get_max_hours(tenant_id: int) -> Decimal:
    """Read tenant setting time.max_hours_per_day (default 24)."""
    try:
        from simorgh.apps.app_settings.services import get_setting
        val = get_setting(f"tenant:{tenant_id}:time.max_hours_per_day")
        if val is not None:
            return Decimal(str(val))
    except Exception:
        pass
    return DEFAULT_MAX_HOURS_PER_DAY


def _daily_hours(tenant_id: int, user_id: int, entry_date: date, exclude_id: int | None = None) -> Decimal:
    """Return total hours already logged for a user on a given day."""
    from simorgh.apps.time_tracking.models import TimeEntry

    qs = TimeEntry.objects.filter(tenant_id=tenant_id, user_id=user_id, date=entry_date)
    if exclude_id:
        qs = qs.exclude(pk=exclude_id)
    total = sum(e.hours for e in qs)
    return Decimal(str(total))


# ---------------------------------------------------------------------------
# Create
# ---------------------------------------------------------------------------

@transaction.atomic
def create_time_entry(
    *,
    tenant_id: int,
    organization_node_id: int,
    user_id: int,
    project_id: int,
    date: date,
    hours: Decimal,
    description: str = "",
    is_billable: bool = True,
    task_id: int | None = None,
) -> "TimeEntry":
    from simorgh.apps.time_tracking.models import TimeEntry
    from simorgh.apps.events.bus import dispatch

    if hours <= 0:
        raise ValueError("Hours must be greater than zero.")

    max_h = _get_max_hours(tenant_id)
    already = _daily_hours(tenant_id, user_id, date)
    if already + hours > max_h:
        raise ValueError(
            f"Exceeded daily maximum of {max_h}h. Already logged {already}h."
        )

    entry = TimeEntry.objects.create(
        tenant_id=tenant_id,
        organization_node_id=organization_node_id,
        user_id=user_id,
        project_id=project_id,
        task_id=task_id,
        date=date,
        hours=hours,
        description=description,
        is_billable=is_billable,
    )

    dispatch("time.entry.logged", {
        "entry_id": entry.pk,
        "user_id": user_id,
        "project_id": project_id,
        "task_id": task_id,
        "hours": float(hours),
        "tenant_id": tenant_id,
    })

    return entry


# ---------------------------------------------------------------------------
# Update
# ---------------------------------------------------------------------------

@transaction.atomic
def update_time_entry(
    entry: "TimeEntry",
    *,
    date: date | None = None,
    hours: Decimal | None = None,
    description: str | None = None,
    is_billable: bool | None = None,
    task_id: int | None = ...,  # type: ignore[assignment]
) -> "TimeEntry":
    if entry.is_approved:
        raise ValueError("Cannot edit an approved time entry.")

    if hours is not None:
        if hours <= 0:
            raise ValueError("Hours must be greater than zero.")
        check_date = date if date is not None else entry.date
        max_h = _get_max_hours(entry.tenant_id)
        already = _daily_hours(entry.tenant_id, entry.user_id, check_date, exclude_id=entry.pk)
        if already + hours > max_h:
            raise ValueError(
                f"Exceeded daily maximum of {max_h}h. Already logged {already}h."
            )
        entry.hours = hours

    if date is not None:
        entry.date = date
    if description is not None:
        entry.description = description
    if is_billable is not None:
        entry.is_billable = is_billable
    if task_id is not ...:
        entry.task_id = task_id

    entry.save()
    return entry


# ---------------------------------------------------------------------------
# Delete
# ---------------------------------------------------------------------------

def delete_time_entry(entry: "TimeEntry") -> None:
    if entry.is_approved:
        raise ValueError("Cannot delete an approved time entry.")
    entry.delete()


# ---------------------------------------------------------------------------
# Approve
# ---------------------------------------------------------------------------

@transaction.atomic
def approve_time_entries(entry_pks: list[int], *, approved_by_id: int, tenant_id: int) -> int:
    """Bulk approve time entries. Returns number of entries approved."""
    from simorgh.apps.time_tracking.models import TimeEntry
    from simorgh.apps.events.bus import dispatch

    entries = TimeEntry.objects.filter(
        pk__in=entry_pks, tenant_id=tenant_id, is_approved=False
    )
    count = 0
    now = timezone.now()
    for entry in entries:
        entry.is_approved = True
        entry.approved_by_id = approved_by_id
        entry.approved_at = now
        entry.save(update_fields=["is_approved", "approved_by_id", "approved_at"])
        dispatch("time.entry.approved", {
            "entry_id": entry.pk,
            "approved_by": approved_by_id,
            "tenant_id": tenant_id,
        })
        count += 1
    return count
