"""
Better error messages with actionable suggestions for common g-gremlin failures.

This module provides helper functions to format errors with suggestions that help
users and AI agents quickly resolve issues.
"""

from __future__ import annotations

from pathlib import Path
from typing import List, Optional

from g_gremlin.paths import resolve_gremlin_home


class GremlinError(Exception):
    """Base exception for g-gremlin with suggestion support."""

    def __init__(self, message: str, suggestion: Optional[str] = None):
        self.message = message
        self.suggestion = suggestion
        super().__init__(self._format_message())

    def _format_message(self) -> str:
        """Format error with suggestion if available."""
        if self.suggestion:
            return f"{self.message}\n\nSuggestion: {self.suggestion}"
        return self.message


class AuthenticationError(GremlinError):
    """Authentication-related errors."""
    pass


class ConfigurationError(GremlinError):
    """Configuration file errors."""
    pass


class ResourceNotFoundError(GremlinError):
    """Resource (sheet, tab, column, etc.) not found errors."""
    pass


def credentials_not_found_error(attempted_path: Optional[Path] = None) -> AuthenticationError:
    """Error when Google OAuth credentials file is missing."""
    config_dir = resolve_gremlin_home()

    if attempted_path:
        message = f"Credentials file not found at: {attempted_path}"
    else:
        message = "No Google OAuth client credentials found."

    suggestion = (
        "1. Download OAuth credentials from Google Cloud Console:\n"
        "   https://console.cloud.google.com/apis/credentials\n"
        f"2. Save as: {config_dir}/credentials.json\n"
        "   OR set G_GREMLIN_CREDENTIALS=/path/to/credentials.json"
    )

    return AuthenticationError(message, suggestion)


def token_expired_error(token_path: Path) -> AuthenticationError:
    """Error when stored token is expired and cannot be refreshed."""
    message = "Stored credentials are invalid or expired and could not be refreshed."

    suggestion = (
        f"Delete the expired token and re-authenticate:\n"
        f"  rm {token_path}\n"
        f"  g-gremlin pull SHEET_ID --range A1  # Will trigger re-auth"
    )

    return AuthenticationError(message, suggestion)


def ci_credentials_missing_error() -> AuthenticationError:
    """Error when CI credentials are required but missing."""
    message = (
        "CI detected but G_GREMLIN_GOOGLE_* credentials not set. "
        "See README: CI Setup for required secrets."
    )

    suggestion = (
        "Set these environment variables in CI:\n"
        "  G_GREMLIN_GOOGLE_CLIENT_ID\n"
        "  G_GREMLIN_GOOGLE_CLIENT_SECRET\n"
        "  G_GREMLIN_GOOGLE_REFRESH_TOKEN"
    )

    return AuthenticationError(message, suggestion)


def config_file_not_found_error(config_path: Path) -> ConfigurationError:
    """Error when sync config file doesn't exist."""
    message = f"Config file not found: {config_path}"

    suggestion = (
        "Create a config file with this structure:\n"
        "  version: 1\n"
        '  sheet_id: "YOUR_SHEET_ID"\n'
        "  tabs:\n"
        "    TabName:\n"
        "      csv: ./data.csv\n\n"
        "Or use an absolute path to an existing config file."
    )

    return ConfigurationError(message, suggestion)


def missing_pyyaml_error() -> ConfigurationError:
    """Error when PyYAML is required but not installed."""
    message = "PyYAML is required for YAML config files but is not installed."

    suggestion = (
        "Install PyYAML:\n"
        "  pip install pyyaml\n\n"
        "Or use a JSON config file instead (.json extension)."
    )

    return ConfigurationError(message, suggestion)


def column_not_found_error(
    column: str,
    available_columns: List[str],
    context: str = "CSV"
) -> ResourceNotFoundError:
    """Error when a required column is missing from CSV/sheet."""
    available = ", ".join(available_columns[:10])  # Limit to first 10
    if len(available_columns) > 10:
        available += f", ... ({len(available_columns)} total)"

    message = f"Column '{column}' not found in {context}."

    # Suggest close matches if available
    close_matches = [col for col in available_columns if column.lower() in col.lower()]
    if close_matches:
        suggestion = (
            f"Did you mean one of these?\n"
            f"  {', '.join(close_matches[:5])}\n\n"
            f"Available columns: {available}"
        )
    else:
        suggestion = f"Available columns: {available}"

    return ResourceNotFoundError(message, suggestion)


def sheet_tab_not_found_error(
    tab_name: str,
    spreadsheet_id: str,
    available_tabs: Optional[List[str]] = None
) -> ResourceNotFoundError:
    """Error when a sheet tab doesn't exist."""
    message = f"Tab '{tab_name}' not found in spreadsheet."

    if available_tabs:
        tabs_list = "\n  ".join(available_tabs[:10])
        suggestion = (
            f"Available tabs in sheet {spreadsheet_id}:\n"
            f"  {tabs_list}\n\n"
            f"Check for typos or ensure the tab exists in the sheet."
        )
    else:
        suggestion = (
            "Verify the tab name exactly matches (case-sensitive):\n"
            f"  g-gremlin pull {spreadsheet_id} --range 'Correct Tab Name'!A1"
        )

    return ResourceNotFoundError(message, suggestion)


def snapshot_not_found_error(snapshot_name: str) -> ResourceNotFoundError:
    """Error when a named snapshot doesn't exist."""
    config_dir = resolve_gremlin_home()
    snapshots_dir = config_dir / "snapshots"

    message = f"Snapshot '{snapshot_name}' not found."

    suggestion = (
        "List available snapshots:\n"
        "  g-gremlin snapshot list\n\n"
        "Or create a new snapshot:\n"
        "  g-gremlin snapshot capture SHEET_ID --name my_snapshot"
    )

    return ResourceNotFoundError(message, suggestion)


def cell_not_found_error(cell_ref: str, sheet_name: str) -> ResourceNotFoundError:
    """Error when a cell reference doesn't exist in sheet data."""
    message = f"Cell {cell_ref} not found in sheet '{sheet_name}'."

    suggestion = (
        "Verify the cell reference is within the sheet's data range:\n"
        f"  g-gremlin pull SHEET_ID --range '{sheet_name}'!A1:Z  # Check available range"
    )

    return ResourceNotFoundError(message, suggestion)


def invalid_range_syntax_error(range_str: str) -> ValueError:
    """Error when A1 notation range syntax is invalid."""
    message = f"Invalid range syntax: {range_str}"

    suggestion = (
        "Use valid A1 notation:\n"
        "  'Sheet Name'!A1:Z100   (quoted if contains spaces)\n"
        "  Data!A:Z               (entire columns)\n"
        "  Leads!1:100            (entire rows)\n"
        "  Summary!B5             (single cell)"
    )

    return ValueError(f"{message}\n\nSuggestion: {suggestion}")


def dry_run_reminder_error(operation: str) -> GremlinError:
    """Reminder that operation requires --apply flag."""
    message = f"Dry-run mode: {operation} not executed (no changes made)."

    suggestion = (
        "To apply changes, add the --apply flag:\n"
        "  g-gremlin [command] --apply"
    )

    return GremlinError(message, suggestion)
