"""Smart Habit tools for Reclaim.ai."""

from typing import Any, Optional

from fastmcp.exceptions import ToolError
from pydantic import ValidationError

from reclaim_mcp.cache import invalidate_cache, ttl_cache
from reclaim_mcp.client import ReclaimClient
from reclaim_mcp.config import get_settings
from reclaim_mcp.exceptions import NotFoundError, RateLimitError, ReclaimError
from reclaim_mcp.models import CalendarEventId, EventInstanceId, HabitCreate, HabitId, HabitUpdate
from reclaim_mcp.utils import format_validation_errors


def _get_client() -> ReclaimClient:
    """Get a configured Reclaim client."""
    settings = get_settings()
    return ReclaimClient(settings)


@ttl_cache(ttl=120)
async def list_habits() -> list[dict]:
    """List all smart habits from Reclaim.ai.

    Returns:
        List of habit objects with lineageId, title, enabled, recurrence, etc.
    """
    try:
        client = _get_client()
        habits = await client.get("/api/smart-habits")
        return habits
    except RateLimitError as e:
        raise ToolError(str(e))
    except ReclaimError as e:
        raise ToolError(f"Error listing habits: {e}")


@ttl_cache(ttl=60)
async def get_habit(lineage_id: int) -> dict:
    """Get a single smart habit by lineage ID.

    Args:
        lineage_id: The habit lineage ID to retrieve

    Returns:
        SmartHabitLineageView object with full details.
    """
    # Validate input using Pydantic model
    try:
        validated = HabitId(lineage_id=lineage_id)
    except ValidationError as e:
        raise ToolError(format_validation_errors(e))

    try:
        client = _get_client()
        habit = await client.get(f"/api/smart-habits/{validated.lineage_id}")
        return habit
    except NotFoundError:
        raise ToolError(f"Habit {validated.lineage_id} not found")
    except RateLimitError as e:
        raise ToolError(str(e))
    except ReclaimError as e:
        raise ToolError(f"Error getting habit {validated.lineage_id}: {e}")


async def create_habit(
    title: str,
    ideal_time: str,
    duration_min_mins: int,
    frequency: str = "WEEKLY",
    ideal_days: Optional[list[str]] = None,
    event_type: str = "SOLO_WORK",
    defense_aggression: str = "DEFAULT",
    duration_max_mins: Optional[int] = None,
    description: Optional[str] = None,
    enabled: bool = True,
    time_policy_type: Optional[str] = None,
) -> dict:
    """Create a new smart habit for auto-scheduling.

    Args:
        title: Habit name/title
        ideal_time: Preferred time in "HH:MM" format (e.g., "09:00")
        duration_min_mins: Minimum duration in minutes
        frequency: Recurrence (DAILY, WEEKLY, MONTHLY, YEARLY) - default WEEKLY
        ideal_days: Days for WEEKLY frequency (MONDAY, TUESDAY, etc.)
        event_type: Type (FOCUS, SOLO_WORK, PERSONAL, etc.) - default SOLO_WORK
        defense_aggression: Protection level (DEFAULT, NONE, LOW, MEDIUM, HIGH, MAX)
        duration_max_mins: Maximum duration in minutes (defaults to min duration)
        description: Optional habit description
        enabled: Whether habit is active (default True)
        time_policy_type: Time policy (WORK, PERSONAL, MEETING). Auto-inferred if not provided.

    Returns:
        Created habit object.
    """
    # Validate input using Pydantic model
    try:
        validated = HabitCreate(
            title=title,
            ideal_time=ideal_time,
            duration_min_mins=duration_min_mins,
            duration_max_mins=duration_max_mins,
            frequency=frequency,  # type: ignore[arg-type]
            ideal_days=ideal_days,  # type: ignore[arg-type]
            event_type=event_type,  # type: ignore[arg-type]
            defense_aggression=defense_aggression,  # type: ignore[arg-type]
            description=description,
            enabled=enabled,
            time_policy_type=time_policy_type,  # type: ignore[arg-type]
        )
    except ValidationError as e:
        raise ToolError(format_validation_errors(e))

    try:
        client = _get_client()

        # Build recurrence object
        recurrence: dict[str, Any] = {"frequency": validated.frequency.value}
        if validated.ideal_days:
            recurrence["idealDays"] = [d.value for d in validated.ideal_days]

        # Determine time policy type based on event type if not explicitly provided
        time_policy = validated.time_policy_type
        if time_policy is None:
            if validated.event_type.value == "PERSONAL":
                time_policy_str = "PERSONAL"
            else:
                time_policy_str = "WORK"
        else:
            time_policy_str = time_policy.value

        # Normalize ideal time to HH:MM:SS format
        ideal_time_normalized = validated.ideal_time
        if len(ideal_time_normalized) == 5:  # HH:MM format
            ideal_time_normalized = f"{ideal_time_normalized}:00"

        # Build request payload
        payload: dict[str, Any] = {
            "title": validated.title,
            "idealTime": ideal_time_normalized,
            "durationMinMins": validated.duration_min_mins,
            "durationMaxMins": validated.duration_max_mins or validated.duration_min_mins,
            "enabled": validated.enabled,
            "recurrence": recurrence,
            "organizer": {"timePolicyType": time_policy_str},
            "eventType": validated.event_type.value,
            "defenseAggression": validated.defense_aggression.value,
        }
        if validated.description:
            payload["description"] = validated.description

        habit = await client.post("/api/smart-habits", data=payload)
        invalidate_cache("list_habits")
        invalidate_cache("get_habit")
        return habit
    except RateLimitError as e:
        raise ToolError(str(e))
    except ReclaimError as e:
        raise ToolError(f"Error creating habit '{title}': {e}")


async def update_habit(
    lineage_id: int,
    title: Optional[str] = None,
    ideal_time: Optional[str] = None,
    duration_min_mins: Optional[int] = None,
    duration_max_mins: Optional[int] = None,
    enabled: Optional[bool] = None,
    frequency: Optional[str] = None,
    ideal_days: Optional[list[str]] = None,
    event_type: Optional[str] = None,
    defense_aggression: Optional[str] = None,
    description: Optional[str] = None,
) -> dict:
    """Update an existing smart habit.

    Args:
        lineage_id: The habit lineage ID to update
        title: New title (optional)
        ideal_time: New preferred time in "HH:MM" format (optional)
        duration_min_mins: New minimum duration in minutes (optional)
        duration_max_mins: New maximum duration in minutes (optional)
        enabled: Enable/disable the habit (optional)
        frequency: New frequency (DAILY, WEEKLY, MONTHLY, YEARLY) (optional)
        ideal_days: New ideal days list (optional)
        event_type: New event type (optional)
        defense_aggression: New defense aggression (optional)
        description: New description (optional)

    Returns:
        Updated habit object.
    """
    # Validate lineage_id
    try:
        validated_id = HabitId(lineage_id=lineage_id)
    except ValidationError as e:
        raise ToolError(format_validation_errors(e))

    # Validate update fields using Pydantic model
    try:
        validated = HabitUpdate(
            title=title,
            ideal_time=ideal_time,
            duration_min_mins=duration_min_mins,
            duration_max_mins=duration_max_mins,
            enabled=enabled,
            frequency=frequency,  # type: ignore[arg-type]
            ideal_days=ideal_days,  # type: ignore[arg-type]
            event_type=event_type,  # type: ignore[arg-type]
            defense_aggression=defense_aggression,  # type: ignore[arg-type]
            description=description,
        )
    except ValidationError as e:
        raise ToolError(format_validation_errors(e))

    try:
        client = _get_client()

        payload: dict[str, Any] = {}
        if validated.title is not None:
            payload["title"] = validated.title
        if validated.ideal_time is not None:
            # Normalize ideal time to HH:MM:SS format
            ideal_time_normalized = validated.ideal_time
            if len(ideal_time_normalized) == 5:  # HH:MM format
                ideal_time_normalized = f"{ideal_time_normalized}:00"
            payload["idealTime"] = ideal_time_normalized
        if validated.duration_min_mins is not None:
            payload["durationMinMins"] = validated.duration_min_mins
        if validated.duration_max_mins is not None:
            payload["durationMaxMins"] = validated.duration_max_mins
        if validated.enabled is not None:
            payload["enabled"] = validated.enabled
        if validated.description is not None:
            payload["description"] = validated.description
        if validated.event_type is not None:
            payload["eventType"] = validated.event_type.value
        if validated.defense_aggression is not None:
            payload["defenseAggression"] = validated.defense_aggression.value

        # Build recurrence object if any recurrence fields provided
        if validated.frequency is not None or validated.ideal_days is not None:
            recurrence: dict[str, Any] = {}
            if validated.frequency is not None:
                recurrence["frequency"] = validated.frequency.value
            if validated.ideal_days is not None:
                recurrence["idealDays"] = [d.value for d in validated.ideal_days]
            payload["recurrence"] = recurrence

        habit = await client.patch(f"/api/smart-habits/{validated_id.lineage_id}", data=payload)
        invalidate_cache("list_habits")
        invalidate_cache("get_habit")
        return habit
    except NotFoundError:
        raise ToolError(f"Habit {validated_id.lineage_id} not found")
    except RateLimitError as e:
        raise ToolError(str(e))
    except ReclaimError as e:
        raise ToolError(f"Error updating habit {validated_id.lineage_id}: {e}")


async def delete_habit(lineage_id: int) -> bool:
    """Delete a smart habit.

    Args:
        lineage_id: The habit lineage ID to delete

    Returns:
        True if deleted successfully.
    """
    # Validate input using Pydantic model
    try:
        validated = HabitId(lineage_id=lineage_id)
    except ValidationError as e:
        raise ToolError(format_validation_errors(e))

    try:
        client = _get_client()
        await client.delete(f"/api/smart-habits/{validated.lineage_id}")
        invalidate_cache("list_habits")
        invalidate_cache("get_habit")
        return True
    except NotFoundError:
        raise ToolError(f"Habit {validated.lineage_id} not found")
    except RateLimitError as e:
        raise ToolError(str(e))
    except ReclaimError as e:
        raise ToolError(f"Error deleting habit {validated.lineage_id}: {e}")


async def mark_habit_done(event_id: str) -> dict:
    """Mark a habit instance as done.

    Use this to mark today's scheduled habit event as completed.

    Args:
        event_id: The event ID of the specific habit instance (from list_personal_events)

    Returns:
        Action result with updated events and series info.
    """
    # Validate input using Pydantic model
    try:
        validated = EventInstanceId(event_id=event_id)
    except ValidationError as e:
        raise ToolError(format_validation_errors(e))

    try:
        client = _get_client()
        result = await client.post(f"/api/smart-habits/planner/{validated.event_id}/done", data={})
        invalidate_cache("list_habits")
        invalidate_cache("get_habit")
        return result
    except NotFoundError:
        raise ToolError(f"Habit event {validated.event_id} not found")
    except RateLimitError as e:
        raise ToolError(str(e))
    except ReclaimError as e:
        raise ToolError(f"Error marking habit done: {e}")


async def skip_habit(event_id: str) -> dict:
    """Skip a habit instance.

    Use this to skip today's scheduled habit event without marking it done.

    Args:
        event_id: The event ID of the specific habit instance to skip

    Returns:
        Action result with updated events and series info.
    """
    # Validate input using Pydantic model
    try:
        validated = EventInstanceId(event_id=event_id)
    except ValidationError as e:
        raise ToolError(format_validation_errors(e))

    try:
        client = _get_client()
        result = await client.post(f"/api/smart-habits/planner/{validated.event_id}/skip", data={})
        invalidate_cache("list_habits")
        invalidate_cache("get_habit")
        return result
    except NotFoundError:
        raise ToolError(f"Habit event {validated.event_id} not found")
    except RateLimitError as e:
        raise ToolError(str(e))
    except ReclaimError as e:
        raise ToolError(f"Error skipping habit: {e}")


async def lock_habit_instance(event_id: str) -> dict:
    """Lock a habit instance to prevent it from being rescheduled.

    Args:
        event_id: The event ID of the specific habit instance to lock

    Returns:
        Action result with updated events and series info.
    """
    # Validate input using Pydantic model
    try:
        validated = EventInstanceId(event_id=event_id)
    except ValidationError as e:
        raise ToolError(format_validation_errors(e))

    try:
        client = _get_client()
        result = await client.post(f"/api/smart-habits/planner/{validated.event_id}/lock", data={})
        invalidate_cache("list_habits")
        invalidate_cache("get_habit")
        return result
    except NotFoundError:
        raise ToolError(f"Habit event {validated.event_id} not found")
    except RateLimitError as e:
        raise ToolError(str(e))
    except ReclaimError as e:
        raise ToolError(f"Error locking habit instance: {e}")


async def unlock_habit_instance(event_id: str) -> dict:
    """Unlock a habit instance to allow it to be rescheduled.

    Args:
        event_id: The event ID of the specific habit instance to unlock

    Returns:
        Action result with updated events and series info.
    """
    # Validate input using Pydantic model
    try:
        validated = EventInstanceId(event_id=event_id)
    except ValidationError as e:
        raise ToolError(format_validation_errors(e))

    try:
        client = _get_client()
        # fmt: off
        result = await client.post(
            f"/api/smart-habits/planner/{validated.event_id}/unlock", data={}
        )
        # fmt: on
        invalidate_cache("list_habits")
        invalidate_cache("get_habit")
        return result
    except NotFoundError:
        raise ToolError(f"Habit event {validated.event_id} not found")
    except RateLimitError as e:
        raise ToolError(str(e))
    except ReclaimError as e:
        raise ToolError(f"Error unlocking habit instance: {e}")


async def start_habit(lineage_id: int) -> dict:
    """Start a habit session now.

    Args:
        lineage_id: The habit lineage ID to start

    Returns:
        Action result with updated events and series info.
    """
    # Validate input using Pydantic model
    try:
        validated = HabitId(lineage_id=lineage_id)
    except ValidationError as e:
        raise ToolError(format_validation_errors(e))

    try:
        client = _get_client()
        # fmt: off
        result = await client.post(
            f"/api/smart-habits/planner/{validated.lineage_id}/start", data={}
        )
        # fmt: on
        invalidate_cache("list_habits")
        invalidate_cache("get_habit")
        return result
    except NotFoundError:
        raise ToolError(f"Habit {validated.lineage_id} not found")
    except RateLimitError as e:
        raise ToolError(str(e))
    except ReclaimError as e:
        raise ToolError(f"Error starting habit {validated.lineage_id}: {e}")


async def stop_habit(lineage_id: int) -> dict:
    """Stop a currently running habit session.

    Args:
        lineage_id: The habit lineage ID to stop

    Returns:
        Action result with updated events and series info.
    """
    # Validate input using Pydantic model
    try:
        validated = HabitId(lineage_id=lineage_id)
    except ValidationError as e:
        raise ToolError(format_validation_errors(e))

    try:
        client = _get_client()
        # fmt: off
        result = await client.post(
            f"/api/smart-habits/planner/{validated.lineage_id}/stop", data={}
        )
        # fmt: on
        invalidate_cache("list_habits")
        invalidate_cache("get_habit")
        return result
    except NotFoundError:
        raise ToolError(f"Habit {validated.lineage_id} not found")
    except RateLimitError as e:
        raise ToolError(str(e))
    except ReclaimError as e:
        raise ToolError(f"Error stopping habit {validated.lineage_id}: {e}")


async def enable_habit(lineage_id: int) -> dict:
    """Enable a disabled habit to resume scheduling.

    Args:
        lineage_id: The habit lineage ID to enable

    Returns:
        Empty dict on success.
    """
    # Validate input using Pydantic model
    try:
        validated = HabitId(lineage_id=lineage_id)
    except ValidationError as e:
        raise ToolError(format_validation_errors(e))

    try:
        client = _get_client()
        result = await client.post(f"/api/smart-habits/{validated.lineage_id}/enable", data={})
        invalidate_cache("list_habits")
        invalidate_cache("get_habit")
        return result
    except NotFoundError:
        raise ToolError(f"Habit {validated.lineage_id} not found")
    except RateLimitError as e:
        raise ToolError(str(e))
    except ReclaimError as e:
        raise ToolError(f"Error enabling habit {validated.lineage_id}: {e}")


async def disable_habit(lineage_id: int) -> bool:
    """Disable a habit to pause scheduling without deleting it.

    Args:
        lineage_id: The habit lineage ID to disable

    Returns:
        True if disabled successfully.
    """
    # Validate input using Pydantic model
    try:
        validated = HabitId(lineage_id=lineage_id)
    except ValidationError as e:
        raise ToolError(format_validation_errors(e))

    try:
        client = _get_client()
        await client.delete(f"/api/smart-habits/{validated.lineage_id}/disable")
        invalidate_cache("list_habits")
        invalidate_cache("get_habit")
        return True
    except NotFoundError:
        raise ToolError(f"Habit {validated.lineage_id} not found")
    except RateLimitError as e:
        raise ToolError(str(e))
    except ReclaimError as e:
        raise ToolError(f"Error disabling habit {validated.lineage_id}: {e}")


async def convert_event_to_habit(
    calendar_id: int,
    event_id: str,
    title: str,
    ideal_time: str,
    duration_min_mins: int,
    frequency: str = "WEEKLY",
    ideal_days: Optional[list[str]] = None,
    event_type: str = "SOLO_WORK",
    defense_aggression: str = "DEFAULT",
    duration_max_mins: Optional[int] = None,
    description: Optional[str] = None,
    enabled: bool = True,
    time_policy_type: Optional[str] = None,
) -> dict:
    """Convert a calendar event into a recurring smart habit.

    Note: Not all events can be converted. The API rejects recurring instances,
    all-day events, and multi-day events. Use with short, timed, standalone events.

    Args:
        calendar_id: The calendar ID containing the event
        event_id: The event ID to convert
        title: Habit name/title
        ideal_time: Preferred time in "HH:MM" format (e.g., "09:00")
        duration_min_mins: Minimum duration in minutes
        frequency: Recurrence frequency (DAILY, WEEKLY, MONTHLY, YEARLY)
        ideal_days: Days of week for WEEKLY frequency (MONDAY, TUESDAY, etc.)
        event_type: Type of event (FOCUS, SOLO_WORK, PERSONAL, etc.)
        defense_aggression: Protection level (DEFAULT, NONE, LOW, MEDIUM, HIGH, MAX)
        duration_max_mins: Maximum duration in minutes (defaults to min duration)
        description: Optional habit description
        enabled: Whether habit is active (default True)
        time_policy_type: Time policy (WORK, PERSONAL, MEETING). Auto-inferred if not provided.

    Returns:
        Created habit object.
    """
    # Validate calendar/event IDs using Pydantic model
    try:
        validated_event = CalendarEventId(calendar_id=calendar_id, event_id=event_id)
    except ValidationError as e:
        raise ToolError(format_validation_errors(e))

    # Validate habit fields using Pydantic model
    try:
        validated = HabitCreate(
            title=title,
            ideal_time=ideal_time,
            duration_min_mins=duration_min_mins,
            duration_max_mins=duration_max_mins,
            frequency=frequency,  # type: ignore[arg-type]
            ideal_days=ideal_days,  # type: ignore[arg-type]
            event_type=event_type,  # type: ignore[arg-type]
            defense_aggression=defense_aggression,  # type: ignore[arg-type]
            description=description,
            enabled=enabled,
            time_policy_type=time_policy_type,  # type: ignore[arg-type]
        )
    except ValidationError as e:
        raise ToolError(format_validation_errors(e))

    try:
        client = _get_client()

        # Build recurrence object
        recurrence: dict[str, Any] = {"frequency": validated.frequency.value}
        if validated.ideal_days:
            recurrence["idealDays"] = [d.value for d in validated.ideal_days]

        # Determine time policy type based on event type if not explicitly provided
        time_policy = validated.time_policy_type
        if time_policy is None:
            if validated.event_type.value == "PERSONAL":
                time_policy_str = "PERSONAL"
            else:
                time_policy_str = "WORK"
        else:
            time_policy_str = time_policy.value

        # Normalize ideal time to HH:MM:SS format
        ideal_time_normalized = validated.ideal_time
        if len(ideal_time_normalized) == 5:  # HH:MM format
            ideal_time_normalized = f"{ideal_time_normalized}:00"

        # Build request payload (same schema as create_habit)
        payload: dict[str, Any] = {
            "title": validated.title,
            "idealTime": ideal_time_normalized,
            "durationMinMins": validated.duration_min_mins,
            "durationMaxMins": validated.duration_max_mins or validated.duration_min_mins,
            "enabled": validated.enabled,
            "recurrence": recurrence,
            "organizer": {"timePolicyType": time_policy_str},
            "eventType": validated.event_type.value,
            "defenseAggression": validated.defense_aggression.value,
        }
        if validated.description:
            payload["description"] = validated.description

        habit = await client.post(
            f"/api/smart-habits/convert/{validated_event.calendar_id}/{validated_event.event_id}",
            data=payload,
        )
        invalidate_cache("list_habits")
        invalidate_cache("get_habit")
        return habit
    except NotFoundError:
        # fmt: off
        raise ToolError(
            f"Event {validated_event.event_id} not found in calendar "
            f"{validated_event.calendar_id}"
        )
        # fmt: on
    except RateLimitError as e:
        raise ToolError(str(e))
    except ReclaimError as e:
        raise ToolError(f"Error converting event to habit: {e}")
