from __future__ import annotations

import argparse
import json
import logging
from pathlib import Path
import sys

from .config import AppConfig, DEFAULT_CONFIG_FILENAME, write_default_config
from .sync_engine import SyncEngine
from .watcher import run_watch_loop

DEFAULT_TARGET = "/Users/den/Library/CloudStorage/SynologyDrive-M/obsidian-sync"
DEFAULT_WINDOWS = r"C:\Users\<user>\SynologyDrive\obsidian-sync"


def build_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(
        prog="obsidian-sync",
        description="Bidirectional sync between a local Obsidian vault and Synology Drive folder.",
    )
    parser.add_argument(
        "--config",
        default=DEFAULT_CONFIG_FILENAME,
        help="Path to config TOML file (default: obsidian-sync.toml).",
    )
    parser.add_argument(
        "--verbose",
        action="store_true",
        help="Enable verbose logging.",
    )

    sub = parser.add_subparsers(dest="command", required=True)

    init_cmd = sub.add_parser("init", help="Create config file and state folders.")
    init_cmd.add_argument("--source", required=True, help="Path to local Obsidian vault.")
    init_cmd.add_argument(
        "--target",
        default=DEFAULT_TARGET,
        help=f"Path to Synology Drive vault mirror (default: {DEFAULT_TARGET}).",
    )
    init_cmd.add_argument(
        "--windows-path",
        default=DEFAULT_WINDOWS,
        help=(
            "Windows Synology local path used for reference/documentation "
            f"(default: {DEFAULT_WINDOWS})."
        ),
    )
    init_cmd.add_argument("--force", action="store_true", help="Overwrite existing config file.")

    sync_now_cmd = sub.add_parser("sync-now", help="Run one sync pass immediately.")
    sync_now_cmd.add_argument("--dry-run", action="store_true", help="Preview actions without writing.")

    watch_cmd = sub.add_parser("watch", help="Run near-real-time sync loop.")
    watch_cmd.add_argument("--dry-run", action="store_true", help="Preview actions without writing.")

    sub.add_parser("status", help="Show config and last sync state.")

    return parser


def main(argv: list[str] | None = None) -> int:
    parser = build_parser()
    args = parser.parse_args(argv)

    configure_logging(verbose=args.verbose)

    if args.command == "init":
        return cmd_init(args)
    if args.command == "sync-now":
        return cmd_sync_now(args)
    if args.command == "watch":
        return cmd_watch(args)
    if args.command == "status":
        return cmd_status(args)

    parser.error("Unknown command")
    return 2


def cmd_init(args: argparse.Namespace) -> int:
    config_path = write_default_config(
        args.config,
        source_vault=args.source,
        synology_vault=args.target,
        windows_synology_vault=args.windows_path,
        force=args.force,
    )

    config = AppConfig.load(config_path)
    config.ensure_directories()

    print(f"Created config: {config.config_path}")
    print(f"Source vault:  {config.paths.source_vault}")
    print(f"Synology path: {config.paths.synology_vault}")
    print(f"State DB:      {config.state_db_path}")
    print("")
    print("Next steps:")
    print(f"  obsidian-sync --config {config.config_path} sync-now")
    print(f"  obsidian-sync --config {config.config_path} watch")
    return 0


def cmd_sync_now(args: argparse.Namespace) -> int:
    config = AppConfig.load(args.config)
    config.ensure_directories()

    engine = SyncEngine(config)
    try:
        stats = engine.sync_once(dry_run=args.dry_run)
    finally:
        engine.close()

    print_sync_stats(stats.to_dict())
    return 1 if stats.errors else 0


def cmd_watch(args: argparse.Namespace) -> int:
    config = AppConfig.load(args.config)
    config.ensure_directories()

    engine = SyncEngine(config)
    try:
        initial = engine.sync_once(dry_run=args.dry_run)
        logging.getLogger(__name__).info("Initial sync complete: %s", initial.to_dict())
        run_watch_loop(engine, dry_run=args.dry_run)
    finally:
        engine.close()

    return 0


def cmd_status(args: argparse.Namespace) -> int:
    config = AppConfig.load(args.config)
    config.ensure_directories()

    engine = SyncEngine(config)
    try:
        last_sync_at = engine.state.get_metadata("last_sync_at")
        last_sync_stats = engine.state.get_metadata("last_sync_stats")
        tracked = engine.state.count_files()
    finally:
        engine.close()

    print(f"Config:             {config.config_path}")
    print(f"Source vault:       {config.paths.source_vault}")
    print(f"Synology mirror:    {config.paths.synology_vault}")
    print(f"Windows reference:  {config.paths.windows_synology_vault or '(not set)'}")
    print(f"State DB:           {config.state_db_path}")
    print(f"Tracked files:      {tracked}")
    print(f"Last sync:          {last_sync_at or '(never)'}")

    if last_sync_stats:
        parsed = json.loads(last_sync_stats)
        print("Last sync stats:")
        print_sync_stats(parsed, indent="  ")

    return 0


def print_sync_stats(stats: dict[str, object], *, indent: str = "") -> None:
    keys = [
        "copied_to_source",
        "copied_to_target",
        "deleted_from_source",
        "deleted_from_target",
        "conflicts",
        "backed_up",
        "skipped",
        "errors",
        "dry_run",
    ]
    for key in keys:
        print(f"{indent}{key}: {stats.get(key)}")


def configure_logging(*, verbose: bool) -> None:
    level = logging.DEBUG if verbose else logging.INFO
    logging.basicConfig(
        level=level,
        format="%(asctime)s %(levelname)s %(name)s - %(message)s",
    )


if __name__ == "__main__":
    sys.exit(main())
