"""Tests for CLI commands — uses CliRunner with temp DB, no Telegram dependency."""

import json
from datetime import datetime, timezone
from pathlib import Path

import pytest
import yaml
from click.testing import CliRunner

from tg_cli.cli.main import cli
from tg_cli.db import MessageDB


@pytest.fixture
def runner():
    return CliRunner()


class TestStats:
    def test_stats_output(self, runner, populated_db, monkeypatch):
        db, db_path = populated_db
        import tg_cli.db as db_mod

        monkeypatch.setattr(db_mod, "get_db_path", lambda: db_path)
        result = runner.invoke(cli, ["stats"])
        assert result.exit_code == 0
        assert "TestGroup" in result.output
        assert "10" in result.output

    def test_stats_yaml(self, runner, populated_db, monkeypatch):
        db, db_path = populated_db
        import tg_cli.db as db_mod

        monkeypatch.setattr(db_mod, "get_db_path", lambda: db_path)
        result = runner.invoke(cli, ["stats", "--yaml"])
        assert result.exit_code == 0
        data = yaml.safe_load(result.output)
        assert data["total"] == 10
        assert data["chats"][0]["chat_name"] == "TestGroup"


class TestSearch:
    def test_search_found(self, runner, populated_db, monkeypatch):
        db, db_path = populated_db
        import tg_cli.db as db_mod

        monkeypatch.setattr(db_mod, "get_db_path", lambda: db_path)
        result = runner.invoke(cli, ["search", "Web3"])
        assert result.exit_code == 0
        assert "Web3" in result.output

    def test_search_not_found(self, runner, populated_db, monkeypatch):
        db, db_path = populated_db
        import tg_cli.db as db_mod

        monkeypatch.setattr(db_mod, "get_db_path", lambda: db_path)
        result = runner.invoke(cli, ["search", "nonexistent_keyword_xyz"])
        assert result.exit_code == 0
        assert "No messages found" in result.output

    def test_search_with_sender_and_hours(self, runner, populated_db, monkeypatch):
        db, db_path = populated_db
        import tg_cli.db as db_mod

        monkeypatch.setattr(db_mod, "get_db_path", lambda: db_path)
        result = runner.invoke(cli, ["search", "Web3", "--sender", "Alice", "--hours", "5"])
        assert result.exit_code == 0
        assert "Found 2 messages" in result.output
        assert "sender=Alice" in result.output
        assert "hours=5" in result.output

    def test_search_chat_not_found(self, runner, populated_db, monkeypatch):
        db, db_path = populated_db
        import tg_cli.db as db_mod

        monkeypatch.setattr(db_mod, "get_db_path", lambda: db_path)
        result = runner.invoke(cli, ["search", "Web3", "--chat", "MissingGroup"])
        assert result.exit_code == 0
        assert "Chat 'MissingGroup' not found in database." in result.output

    def test_search_regex_found(self, runner, populated_db, monkeypatch):
        db, db_path = populated_db
        import tg_cli.db as db_mod

        monkeypatch.setattr(db_mod, "get_db_path", lambda: db_path)
        result = runner.invoke(
            cli,
            ["search", r"Message [12]: (Python|Web3)", "--regex", "--limit", "2"],
        )
        assert result.exit_code == 0
        assert "mode=regex" in result.output

    def test_search_regex_invalid(self, runner, populated_db, monkeypatch):
        db, db_path = populated_db
        import tg_cli.db as db_mod

        monkeypatch.setattr(db_mod, "get_db_path", lambda: db_path)
        result = runner.invoke(cli, ["search", "(", "--regex"])
        assert result.exit_code == 0
        assert "Invalid regex pattern" in result.output

    def test_search_yaml(self, runner, populated_db, monkeypatch):
        db, db_path = populated_db
        import tg_cli.db as db_mod

        monkeypatch.setattr(db_mod, "get_db_path", lambda: db_path)
        result = runner.invoke(cli, ["search", "Web3", "--yaml"])
        assert result.exit_code == 0
        data = yaml.safe_load(result.output)
        assert isinstance(data, list)
        assert data[0]["content"]


class TestRecent:
    def test_recent_found(self, runner, populated_db, monkeypatch):
        db, db_path = populated_db
        import tg_cli.db as db_mod

        monkeypatch.setattr(db_mod, "get_db_path", lambda: db_path)
        result = runner.invoke(cli, ["recent", "--hours", "3", "--limit", "3"])
        assert result.exit_code == 0
        assert "Showing 2 recent messages" in result.output
        assert "hours=3" in result.output

    def test_recent_with_sender_filter(self, runner, populated_db, monkeypatch):
        db, db_path = populated_db
        import tg_cli.db as db_mod

        monkeypatch.setattr(db_mod, "get_db_path", lambda: db_path)
        result = runner.invoke(cli, ["recent", "--sender", "Ali", "--hours", "5"])
        assert result.exit_code == 0
        assert "sender=Ali" in result.output

    def test_recent_chat_not_found(self, runner, populated_db, monkeypatch):
        db, db_path = populated_db
        import tg_cli.db as db_mod

        monkeypatch.setattr(db_mod, "get_db_path", lambda: db_path)
        result = runner.invoke(cli, ["recent", "--chat", "MissingGroup"])
        assert result.exit_code == 0
        assert "Chat 'MissingGroup' not found in database." in result.output


class TestQueryChatNotFound:
    def test_today_chat_not_found(self, runner, populated_db, monkeypatch):
        db, db_path = populated_db
        import tg_cli.db as db_mod

        monkeypatch.setattr(db_mod, "get_db_path", lambda: db_path)
        result = runner.invoke(cli, ["today", "--chat", "MissingGroup"])
        assert result.exit_code == 0
        assert "Chat 'MissingGroup' not found in database." in result.output

    def test_top_chat_not_found(self, runner, populated_db, monkeypatch):
        db, db_path = populated_db
        import tg_cli.db as db_mod

        monkeypatch.setattr(db_mod, "get_db_path", lambda: db_path)
        result = runner.invoke(cli, ["top", "--chat", "MissingGroup"])
        assert result.exit_code == 0
        assert "Chat 'MissingGroup' not found in database." in result.output

    def test_timeline_chat_not_found(self, runner, populated_db, monkeypatch):
        db, db_path = populated_db
        import tg_cli.db as db_mod

        monkeypatch.setattr(db_mod, "get_db_path", lambda: db_path)
        result = runner.invoke(cli, ["timeline", "--chat", "MissingGroup"])
        assert result.exit_code == 0
        assert "Chat 'MissingGroup' not found in database." in result.output

    def test_filter_chat_not_found(self, runner, populated_db, monkeypatch):
        db, db_path = populated_db
        import tg_cli.db as db_mod

        monkeypatch.setattr(db_mod, "get_db_path", lambda: db_path)
        result = runner.invoke(cli, ["filter", "Web3", "--chat", "MissingGroup"])
        assert result.exit_code == 0
        assert "Chat 'MissingGroup' not found in database." in result.output


class TestTodayHints:
    def test_today_shows_refresh_hint_when_local_data_is_old(self, runner, tmp_path, monkeypatch):
        db_path = tmp_path / "test.db"
        db = MessageDB(db_path=db_path)
        db.insert_message(
            chat_id=100,
            chat_name="OldGroup",
            msg_id=1,
            sender_id=1,
            sender_name="Alice",
            content="old message",
            timestamp=datetime(2026, 3, 8, 0, 0, tzinfo=timezone.utc),
        )

        import tg_cli.db as db_mod

        monkeypatch.setattr(db_mod, "get_db_path", lambda: db_path)
        result = runner.invoke(cli, ["today"])
        assert result.exit_code == 0
        assert "No messages today." in result.output
        assert "Latest local message is from" in result.output
        assert "Run 'tg refresh'" in result.output
        assert "refresh." in result.output

    def test_today_shows_empty_db_hint(self, runner, tmp_path, monkeypatch):
        db_path = tmp_path / "test.db"
        MessageDB(db_path=db_path).close()

        import tg_cli.db as db_mod

        monkeypatch.setattr(db_mod, "get_db_path", lambda: db_path)
        result = runner.invoke(cli, ["today"])
        assert result.exit_code == 0
        assert "No messages today." in result.output
        assert "Local database is empty. Run 'tg refresh' first." in result.output


class TestRefreshAndSyncFirst:
    def test_refresh_yaml(self, runner, monkeypatch):
        import tg_cli.cli.tg as tg_mod

        async def fake_sync_all_dialogs(*, limit, on_chat_done=None):
            assert limit == 5000
            return {"ChatA": 2, "ChatB": 0}

        monkeypatch.setattr(tg_mod, "sync_all_dialogs", fake_sync_all_dialogs)
        result = runner.invoke(cli, ["refresh", "--yaml"])
        assert result.exit_code == 0
        data = yaml.safe_load(result.output)
        assert data["new_messages"] == 2
        assert data["updated_chats"] == ["ChatA"]

    def test_today_sync_first_refreshes_before_query(self, runner, tmp_path, monkeypatch):
        db_path = tmp_path / "test.db"
        MessageDB(db_path=db_path).close()

        import tg_cli.cli.query as query_mod
        import tg_cli.db as db_mod

        monkeypatch.setattr(db_mod, "get_db_path", lambda: db_path)

        async def fake_sync_all_dialogs(*, limit, on_chat_done=None):
            with MessageDB(db_path=db_path) as db:
                db.insert_message(
                    chat_id=100,
                    chat_name="FreshGroup",
                    msg_id=1,
                    sender_id=1,
                    sender_name="Alice",
                    content="new today",
                    timestamp=datetime.now(timezone.utc),
                )
            return {"FreshGroup": 1}

        monkeypatch.setattr(query_mod, "sync_all_dialogs", fake_sync_all_dialogs)
        result = runner.invoke(cli, ["today", "--sync-first"])
        assert result.exit_code == 0
        assert "FreshGroup" in result.output

    def test_search_sync_first_syncs_single_chat(self, runner, tmp_path, monkeypatch):
        db_path = tmp_path / "test.db"
        MessageDB(db_path=db_path).close()

        import tg_cli.cli.query as query_mod
        import tg_cli.db as db_mod

        monkeypatch.setattr(db_mod, "get_db_path", lambda: db_path)

        async def fake_sync_chat_dialog(chat, *, limit, on_progress=None):
            assert chat == "FreshGroup"
            with MessageDB(db_path=db_path) as db:
                db.insert_message(
                    chat_id=100,
                    chat_name="FreshGroup",
                    msg_id=1,
                    sender_id=1,
                    sender_name="Alice",
                    content="fresh web3 note",
                    timestamp=datetime.now(timezone.utc),
                )
            return 1

        monkeypatch.setattr(query_mod, "sync_chat_dialog", fake_sync_chat_dialog)
        result = runner.invoke(cli, ["search", "web3", "--chat", "FreshGroup", "--sync-first"])
        assert result.exit_code == 0
        assert "fresh web3 note" in result.output

    def test_stats_sync_first_refreshes_before_summary(self, runner, tmp_path, monkeypatch):
        db_path = tmp_path / "test.db"
        MessageDB(db_path=db_path).close()

        import tg_cli.cli.query as query_mod
        import tg_cli.db as db_mod

        monkeypatch.setattr(db_mod, "get_db_path", lambda: db_path)

        async def fake_sync_all_dialogs(*, limit, on_chat_done=None):
            with MessageDB(db_path=db_path) as db:
                db.insert_message(
                    chat_id=100,
                    chat_name="FreshGroup",
                    msg_id=1,
                    sender_id=1,
                    sender_name="Alice",
                    content="fresh web3 note",
                    timestamp=datetime.now(timezone.utc),
                )
            return {"FreshGroup": 1}

        monkeypatch.setattr(query_mod, "sync_all_dialogs", fake_sync_all_dialogs)
        result = runner.invoke(cli, ["stats", "--sync-first", "--yaml"])
        assert result.exit_code == 0
        data = yaml.safe_load(result.output)
        assert data["total"] == 1
        assert data["chats"][0]["chat_name"] == "FreshGroup"


class TestListenPersist:
    def test_listen_persist_reconnects_until_stopped(self, runner, monkeypatch):
        import contextlib

        import tg_cli.cli.tg as tg_mod

        calls: list[str] = []
        sleeps: list[int] = []

        @contextlib.asynccontextmanager
        async def fake_connect():
            yield object()

        async def fake_listen(client, chats=None):
            calls.append("listen")
            return "disconnected" if len(calls) == 1 else "stopped"

        monkeypatch.setattr(tg_mod, "connect", fake_connect)
        monkeypatch.setattr(tg_mod, "listen", fake_listen)
        monkeypatch.setattr(tg_mod.time, "sleep", lambda seconds: sleeps.append(seconds))

        result = runner.invoke(cli, ["listen", "--persist", "--retry-seconds", "1"])
        assert result.exit_code == 0
        assert len(calls) == 2
        assert sleeps == [1]
        assert "Reconnecting in 1s" in result.output


class TestAmbiguousChat:
    def test_search_ambiguous_chat(self, runner, tmp_path, monkeypatch):
        db_path = tmp_path / "test.db"
        db = MessageDB(db_path=db_path)
        db.insert_message(
            chat_id=100,
            chat_name="Dev Group",
            msg_id=1,
            sender_id=1,
            sender_name="Alice",
            content="hello",
            timestamp=datetime(2026, 3, 10, 0, 0, tzinfo=timezone.utc),
        )
        db.insert_message(
            chat_id=200,
            chat_name="Dev Chat",
            msg_id=2,
            sender_id=2,
            sender_name="Bob",
            content="world",
            timestamp=datetime(2026, 3, 10, 1, 0, tzinfo=timezone.utc),
        )

        import tg_cli.db as db_mod

        monkeypatch.setattr(db_mod, "get_db_path", lambda: db_path)
        result = runner.invoke(cli, ["search", "hello", "--chat", "Dev"])
        assert result.exit_code == 0
        assert "matches multiple local chats" in result.output
        assert "Dev Group" in result.output
        assert "Dev Chat" in result.output


class TestExport:
    def test_export_text(self, runner, populated_db, tmp_path, monkeypatch):
        db, db_path = populated_db
        import tg_cli.db as db_mod

        monkeypatch.setattr(db_mod, "get_db_path", lambda: db_path)
        out_file = str(tmp_path / "export.txt")
        result = runner.invoke(cli, ["export", "TestGroup", "-o", out_file])
        assert result.exit_code == 0
        assert "Exported" in result.output

        content = Path(out_file).read_text()
        assert "Alice:" in content

    def test_export_json(self, runner, populated_db, tmp_path, monkeypatch):
        db, db_path = populated_db
        import tg_cli.db as db_mod

        monkeypatch.setattr(db_mod, "get_db_path", lambda: db_path)
        out_file = str(tmp_path / "export.json")
        result = runner.invoke(cli, ["export", "TestGroup", "-f", "json", "-o", out_file])
        assert result.exit_code == 0

        data = json.loads(Path(out_file).read_text())
        assert isinstance(data, list)
        assert len(data) > 0

    def test_export_not_found(self, runner, populated_db, monkeypatch):
        db, db_path = populated_db
        import tg_cli.db as db_mod

        monkeypatch.setattr(db_mod, "get_db_path", lambda: db_path)
        result = runner.invoke(cli, ["export", "NonexistentGroup"])
        assert result.exit_code == 0
        assert "not found" in result.output

    def test_export_yaml(self, runner, populated_db, tmp_path, monkeypatch):
        db, db_path = populated_db
        import tg_cli.db as db_mod

        monkeypatch.setattr(db_mod, "get_db_path", lambda: db_path)
        out_file = str(tmp_path / "export.yaml")
        result = runner.invoke(cli, ["export", "TestGroup", "-f", "yaml", "-o", out_file])
        assert result.exit_code == 0

        data = yaml.safe_load(Path(out_file).read_text())
        assert isinstance(data, list)
        assert data[0]["chat_name"] == "TestGroup"


class TestHelp:
    def test_main_help(self, runner):
        result = runner.invoke(cli, ["--help"])
        assert result.exit_code == 0
        assert "tg" in result.output

    def test_tg_help(self, runner):
        result = runner.invoke(cli, ["chats", "--help"])
        assert result.exit_code == 0
        assert "chats" in result.output.lower() or "telegram" in result.output.lower()

    def test_today_help(self, runner):
        result = runner.invoke(cli, ["today", "--help"])
        assert result.exit_code == 0
        assert "today" in result.output.lower() or "chat" in result.output.lower()
