# Familiar - Self-Hosted AI Companion Platform
# Copyright (c) 2026 George Scott Foley
# ORCID: 0009-0006-4957-0540
# Email: Georgescottfoley@proton.me
# Licensed under the MIT License - see LICENSE file for details

"""
Task Management Skill

Simple task/todo tracking with categories, priorities, and due dates.
Tasks are stored in ~/.familiar/data/tasks.json
"""

import json
import logging
from datetime import datetime, timedelta
from typing import Optional

# Use centralized paths


def _get_data_dir():
    """Get data directory (tenant-scoped in Reflection, default in standalone)."""
    try:
        from familiar.core import paths

        return paths.DATA_DIR
    except ImportError:
        return Path.home() / ".familiar" / "data"


try:
    from familiar.core.paths import DATA_DIR, ensure_dir
    from familiar.core.utils import atomic_write_json

    TASKS_FILE = DATA_DIR / "tasks.json"
except ImportError:
    # Fallback for standalone testing
    from pathlib import Path

    DATA_DIR = _get_data_dir()
    TASKS_FILE = DATA_DIR / "tasks.json"

    def ensure_dir(path: Path) -> Path:
        path.mkdir(parents=True, exist_ok=True)
        return path

    atomic_write_json = None

logger = logging.getLogger(__name__)


def _load_tasks() -> list:
    """Load tasks from file."""
    if TASKS_FILE.exists():
        try:
            with open(TASKS_FILE, encoding="utf-8") as f:
                return json.load(f)
        except (json.JSONDecodeError, IOError, OSError) as e:
            logger.warning(f"Failed to load tasks: {e}")
            return []
    return []


def _save_tasks(tasks: list):
    """Save tasks to file atomically."""
    ensure_dir(TASKS_FILE.parent)
    if atomic_write_json:
        atomic_write_json(TASKS_FILE, tasks, indent=2)
    else:
        with open(TASKS_FILE, "w", encoding="utf-8") as f:
            json.dump(tasks, f, indent=2)


def _generate_id() -> str:
    """Generate a short task ID."""
    import random
    import string

    return "".join(random.choices(string.ascii_lowercase + string.digits, k=6))


def _priority_emoji(priority: str) -> str:
    """Get emoji for priority level."""
    return {"urgent": "🔴", "high": "🟡", "normal": "🟢", "low": "⚪"}.get(priority.lower(), "🟢")


def _parse_due_date(due_str: str) -> Optional[str]:
    """Parse various date formats into ISO format."""
    if not due_str:
        return None

    due_str = due_str.lower().strip()
    today = datetime.now().date()

    # Relative dates
    if due_str == "today":
        return today.isoformat()
    elif due_str == "tomorrow":
        return (today + timedelta(days=1)).isoformat()
    elif due_str == "next week":
        return (today + timedelta(weeks=1)).isoformat()
    elif due_str == "next month":
        return (today + timedelta(days=30)).isoformat()
    elif due_str.endswith(" days"):
        try:
            days = int(due_str.split()[0])
            return (today + timedelta(days=days)).isoformat()
        except (ValueError, IndexError):
            pass

    # Try parsing as date
    for fmt in ["%Y-%m-%d", "%m/%d/%Y", "%m/%d", "%B %d", "%b %d"]:
        try:
            parsed = datetime.strptime(due_str, fmt)
            if parsed.year == 1900:  # No year specified
                parsed = parsed.replace(year=today.year)
                if parsed.date() < today:
                    parsed = parsed.replace(year=today.year + 1)
            return parsed.date().isoformat()
        except ValueError:
            continue

    return due_str  # Return as-is if we can't parse


def add_task(data: dict) -> str:
    """Add a new task."""
    title = data.get("title", "").strip()
    if not title:
        return "Please provide a task title."

    task = {
        "id": _generate_id(),
        "title": title,
        "description": data.get("description", ""),
        "category": data.get("category", "general"),
        "priority": data.get("priority", "normal").lower(),
        "due_date": _parse_due_date(data.get("due_date", "")),
        "created_at": datetime.now().isoformat(),
        "completed": False,
        "completed_at": None,
        "tags": data.get("tags", []),
    }

    tasks = _load_tasks()
    tasks.append(task)
    _save_tasks(tasks)

    due_str = f" (due: {task['due_date']})" if task["due_date"] else ""
    return f"✅ Added task [{task['id']}]: {title}{due_str}"


def list_tasks(data: dict) -> str:
    """List tasks with optional filters."""
    tasks = _load_tasks()

    # Filters
    show_completed = data.get("show_completed", False)
    category = data.get("category", "").lower()
    priority = data.get("priority", "").lower()
    due_soon = data.get("due_soon", False)  # Due within 7 days

    # Filter tasks
    filtered = []
    today = datetime.now().date()

    for task in tasks:
        if not show_completed and task.get("completed"):
            continue
        if category and task.get("category", "").lower() != category:
            continue
        if priority and task.get("priority", "").lower() != priority:
            continue
        if due_soon and task.get("due_date"):
            try:
                due = datetime.fromisoformat(task["due_date"]).date()
                if due > today + timedelta(days=7):
                    continue
            except (ValueError, TypeError):
                pass

        filtered.append(task)

    if not filtered:
        return "📋 No tasks found matching your criteria."

    # Sort by due date, then priority
    priority_order = {"urgent": 0, "high": 1, "normal": 2, "low": 3}

    def sort_key(t):
        due = t.get("due_date") or "9999-99-99"
        pri = priority_order.get(t.get("priority", "normal"), 2)
        return (due, pri)

    filtered.sort(key=sort_key)

    # Format output
    lines = [f"📋 Tasks ({len(filtered)}):\n"]

    for task in filtered:
        emoji = _priority_emoji(task.get("priority", "normal"))
        status = "✓" if task.get("completed") else "○"
        due = ""

        if task.get("due_date"):
            try:
                due_date = datetime.fromisoformat(task["due_date"]).date()
                days_until = (due_date - today).days

                if days_until < 0:
                    due = f" ⚠️ OVERDUE ({abs(days_until)}d)"
                elif days_until == 0:
                    due = " 📅 TODAY"
                elif days_until == 1:
                    due = " 📅 Tomorrow"
                elif days_until <= 7:
                    due = f" 📅 {days_until}d"
                else:
                    due = f" 📅 {task['due_date']}"
            except (ValueError, TypeError):
                due = f" 📅 {task['due_date']}"

        cat = f"[{task.get('category', '')}] " if task.get("category") else ""
        lines.append(f"{emoji} {status} [{task['id']}] {cat}{task['title']}{due}")

    return "\n".join(lines)


def complete_task(data: dict) -> str:
    """Mark a task as complete."""
    task_id = data.get("id", "").lower()

    if not task_id:
        return "Please provide the task ID to complete."

    tasks = _load_tasks()

    for task in tasks:
        if task["id"] == task_id:
            if task.get("completed"):
                return f"Task [{task_id}] is already completed."

            task["completed"] = True
            task["completed_at"] = datetime.now().isoformat()
            _save_tasks(tasks)
            return f"✅ Completed: {task['title']}"

    # Try partial match
    matches = [t for t in tasks if task_id in t["id"] or task_id.lower() in t["title"].lower()]

    if len(matches) == 1:
        task = matches[0]
        task["completed"] = True
        task["completed_at"] = datetime.now().isoformat()
        _save_tasks(tasks)
        return f"✅ Completed: {task['title']}"
    elif len(matches) > 1:
        return "Multiple matches found. Please be more specific:\n" + "\n".join(
            f"  [{t['id']}] {t['title']}" for t in matches
        )

    return f"Error: Task not found: {task_id}"


def update_task(data: dict) -> str:
    """Update a task's details."""
    task_id = data.get("id", "").lower()

    if not task_id:
        return "Error: Please provide the task ID to update."

    tasks = _load_tasks()

    for task in tasks:
        if task["id"] == task_id:
            # Update fields if provided
            if "title" in data:
                task["title"] = data["title"]
            if "description" in data:
                task["description"] = data["description"]
            if "priority" in data:
                task["priority"] = data["priority"].lower()
            if "category" in data:
                task["category"] = data["category"]
            if "due_date" in data:
                task["due_date"] = _parse_due_date(data["due_date"])

            _save_tasks(tasks)
            return f"✅ Updated task [{task_id}]: {task['title']}"

    return f"Error: Task not found: {task_id}"


def delete_task(data: dict) -> str:
    """Delete a task."""
    task_id = data.get("id", "").lower()

    if not task_id:
        return "Please provide the task ID to delete."

    tasks = _load_tasks()

    for i, task in enumerate(tasks):
        if task["id"] == task_id:
            if not data.get("_confirmed"):
                from familiar.core.confirmations import needs_confirmation

                return needs_confirmation(
                    tool_name="delete_task",
                    tool_input=data,
                    preview=f"Delete task: {task_id}",
                    risk="medium",
                )

            tasks.pop(i)
            _save_tasks(tasks)
            return f"🗑️ Deleted: {task['title']}"

    return f"Error: Task not found: {task_id}"


def get_daily_briefing(data: dict) -> str:
    """Get a summary of today's tasks and upcoming deadlines."""
    tasks = _load_tasks()
    today = datetime.now().date()

    overdue = []
    due_today = []
    due_this_week = []
    urgent = []

    for task in tasks:
        if task.get("completed"):
            continue

        # Check priority
        if task.get("priority") == "urgent":
            urgent.append(task)

        # Check due dates
        if task.get("due_date"):
            try:
                due = datetime.fromisoformat(task["due_date"]).date()
                days_until = (due - today).days

                if days_until < 0:
                    overdue.append((task, abs(days_until)))
                elif days_until == 0:
                    due_today.append(task)
                elif days_until <= 7:
                    due_this_week.append((task, days_until))
            except (ValueError, TypeError):
                pass

    lines = ["📅 Daily Briefing\n"]

    if overdue:
        lines.append("⚠️ OVERDUE:")
        for task, days in sorted(overdue, key=lambda x: x[1], reverse=True):
            lines.append(f"  🔴 [{task['id']}] {task['title']} ({days}d overdue)")
        lines.append("")

    if due_today:
        lines.append("📌 DUE TODAY:")
        for task in due_today:
            emoji = _priority_emoji(task.get("priority", "normal"))
            lines.append(f"  {emoji} [{task['id']}] {task['title']}")
        lines.append("")

    if urgent and not any(t in [x[0] for x in overdue] + due_today for t in urgent):
        lines.append("🔴 URGENT:")
        for task in urgent:
            lines.append(f"  [{task['id']}] {task['title']}")
        lines.append("")

    if due_this_week:
        lines.append("📆 This Week:")
        for task, days in sorted(due_this_week, key=lambda x: x[1]):
            emoji = _priority_emoji(task.get("priority", "normal"))
            lines.append(f"  {emoji} [{task['id']}] {task['title']} ({days}d)")

    if len(lines) == 1:
        lines.append("✨ All clear! No urgent tasks or upcoming deadlines.")

    return "\n".join(lines)


# Tool definitions
TOOLS = [
    {
        "name": "add_task",
        "description": (
            "Add a new task or to-do item with optional category, priority, due date, "
            "and tags. Use this when the user wants to create a reminder, track an action "
            "item, or add something to their to-do list. Returns the new task ID and "
            "title for future reference."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "title": {"type": "string", "description": "Task title/description"},
                "description": {"type": "string", "description": "Additional details"},
                "category": {
                    "type": "string",
                    "description": "Category (grants, donors, board, events, compliance, programs, general)",
                },
                "priority": {
                    "type": "string",
                    "enum": ["urgent", "high", "normal", "low"],
                    "default": "normal",
                },
                "due_date": {
                    "type": "string",
                    "description": "Due date (today, tomorrow, next week, 2024-03-15, March 15, etc.)",
                },
                "tags": {
                    "type": "array",
                    "items": {"type": "string", "description": "Tag value"},
                    "description": "Optional tags",
                },
            },
            "required": ["title"],
        },
        "handler": add_task,
        "category": "tasks",
    },
    {
        "name": "list_tasks",
        "description": (
            "List tasks with optional filters for category, priority, completion status, "
            "and upcoming deadlines. Use this when the user asks to see their tasks, "
            "review what's pending, or check a specific category. Returns tasks sorted "
            "by due date and priority with overdue indicators."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "show_completed": {"type": "boolean", "default": False},
                "category": {"type": "string", "description": "Filter by category"},
                "priority": {"type": "string", "description": "Filter by priority"},
                "due_soon": {
                    "type": "boolean",
                    "description": "Only show tasks due within 7 days",
                    "default": False,
                },
            },
        },
        "handler": list_tasks,
        "category": "tasks",
    },
    {
        "name": "complete_task",
        "description": (
            "Mark a task as complete by its ID or a partial title match. "
            "Use this when the user says they finished a task or want to check it off. "
            "Supports fuzzy matching if the exact ID is not provided. "
            "Returns confirmation with the completed task title."
        ),
        "input_schema": {
            "type": "object",
            "properties": {"id": {"type": "string", "description": "Task ID or partial title"}},
            "required": ["id"],
        },
        "handler": complete_task,
        "category": "tasks",
    },
    {
        "name": "update_task",
        "description": (
            "Update a task's title, description, priority, category, or due date. "
            "Use this when the user wants to change details on an existing task, "
            "reschedule a deadline, or re-prioritize. Only the fields provided will "
            "be updated; omitted fields remain unchanged."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "id": {"type": "string", "description": "Task ID"},
                "title": {"type": "string", "description": "New task title"},
                "description": {"type": "string", "description": "New task description or details"},
                "priority": {
                    "type": "string",
                    "description": "New priority level (urgent, high, normal, low)",
                },
                "category": {"type": "string", "description": "New category for the task"},
                "due_date": {
                    "type": "string",
                    "description": "New due date (today, tomorrow, next week, 2024-03-15, etc.)",
                },
            },
            "required": ["id"],
        },
        "handler": update_task,
        "category": "tasks",
    },
    {
        "name": "delete_task",
        "description": (
            "Permanently delete a task by its ID. Use this when the user wants to remove "
            "a task entirely rather than marking it complete. Requires confirmation before "
            "the task is deleted. Returns the title of the deleted task."
        ),
        "requires_confirmation": True,
        "input_schema": {
            "type": "object",
            "properties": {
                "id": {"type": "string", "description": "Task ID"},
            },
            "required": ["id"],
        },
        "handler": delete_task,
        "category": "tasks",
    },
    {
        "name": "task_briefing",
        "description": (
            "Get a daily briefing summarizing overdue tasks, items due today, urgent "
            "tasks, and upcoming deadlines within the next seven days. Use this for "
            "morning check-ins or when the user asks for a task overview. "
            "Returns a prioritized summary grouped by urgency."
        ),
        "input_schema": {"type": "object", "properties": {}},
        "handler": get_daily_briefing,
        "category": "tasks",
    },
]
