"""Tests for CLI interface."""

import pytest
import click
from click.testing import CliRunner
from panchanga_cli.cli import (
    cli,
    get_place_coordinates,
    validate_date,
    validate_time,
    parse_date_string,
    parse_try_string,
)


class TestCLI:
    def test_cli_help(self):
        """Test CLI help command."""
        runner = CliRunner()
        result = runner.invoke(cli, ["--help"])
        assert result.exit_code == 0
        assert "Panchanga - Traditional Vedic Calendar" in result.output

    def test_cli_version(self):
        """Test CLI version command."""
        runner = CliRunner()
        result = runner.invoke(cli, ["version"])
        assert result.exit_code == 0
        assert "Panchanga Python Version" in result.output

    def test_cli_places(self):
        """Test CLI places command."""
        runner = CliRunner()
        result = runner.invoke(cli, ["places"])
        assert result.exit_code == 0
        assert "Available places:" in result.output
        assert "ujjain" in result.output
        assert "delhi" in result.output

    def test_cli_settings(self):
        """Test CLI settings command."""
        runner = CliRunner()
        result = runner.invoke(cli, ["settings"])
        assert result.exit_code == 0
        assert "Current settings:" in result.output
        assert "Latitude:" in result.output
        assert "Longitude:" in result.output

    def test_cli_list_basic(self):
        """Test CLI list command with basic date."""
        runner = CliRunner()
        result = runner.invoke(cli, ["list", "2024 3 15"])
        assert result.exit_code == 0
        assert "2024" in result.output

    def test_cli_verbose_basic(self):
        """Test CLI verbose command with basic date."""
        runner = CliRunner()
        result = runner.invoke(cli, ["verbose", "2024 3 15"])
        assert result.exit_code == 0
        assert "AD 2024" in result.output

    def test_cli_try_basic(self):
        """Test CLI try command with basic Vedic date."""
        runner = CliRunner()
        result = runner.invoke(cli, ["try", "--paksa", "sukla", "1946", "2", "5"])
        assert result.exit_code == 0
        assert "Saka 1946" in result.output

    def test_cli_horoscope_basic(self):
        """Test CLI horoscope command with basic date and time."""
        runner = CliRunner()
        result = runner.invoke(cli, ["horoscope", "2024 3 15 14 30"])
        assert result.exit_code == 0
        assert "Date:" in result.output
        assert "Time:" in result.output

    def test_cli_with_place(self):
        """Test CLI with place name."""
        runner = CliRunner()
        result = runner.invoke(cli, ["--place", "delhi", "verbose", "2024 3 15"])
        assert result.exit_code == 0
        assert "Using coordinates for Delhi" in result.output

    def test_cli_with_coordinates(self):
        """Test CLI with explicit coordinates."""
        runner = CliRunner()
        result = runner.invoke(
            cli, ["--latitude", "28.6", "--longitude", "77.2", "verbose", "2024 3 15"]
        )
        assert result.exit_code == 0

    def test_cli_json_output(self):
        """Test CLI with JSON output."""
        runner = CliRunner()
        result = runner.invoke(cli, ["--json", "settings"])
        assert result.exit_code == 0
        assert '"latitude"' in result.output
        assert '"longitude"' in result.output

    def test_cli_quiet_mode(self):
        """Test CLI with quiet mode."""
        runner = CliRunner()
        result = runner.invoke(cli, ["--quiet", "--place", "delhi", "verbose", "2024 3 15"])
        assert result.exit_code == 0
        assert "Using coordinates for Delhi" not in result.output

    def test_cli_system_option(self):
        """Test CLI with system option."""
        runner = CliRunner()
        result = runner.invoke(cli, ["--system", "surya", "settings"])
        assert result.exit_code == 0
        assert "System: surya" in result.output

    def test_cli_invalid_date(self):
        """Test CLI with invalid date."""
        runner = CliRunner()
        result = runner.invoke(cli, ["list", "2024 13 15"])
        assert result.exit_code == 1
        assert "Error:" in result.output

    def test_cli_invalid_place(self):
        """Test CLI with invalid place."""
        runner = CliRunner()
        result = runner.invoke(cli, ["--place", "nonexistent", "verbose", "2024 3 15"])
        assert result.exit_code == 1
        assert "Error:" in result.output

    def test_cli_invalid_coordinates(self):
        """Test CLI with invalid coordinates."""
        runner = CliRunner()
        result = runner.invoke(cli, ["--latitude", "100", "verbose", "2024 3 15"])
        assert result.exit_code == 2  # Click uses exit code 2 for parameter errors
        assert "Error:" in result.output

    def test_cli_invalid_system(self):
        """Test CLI with invalid system."""
        runner = CliRunner()
        result = runner.invoke(cli, ["--system", "invalid", "settings"])
        assert result.exit_code == 2  # Click uses exit code 2 for parameter errors
        assert "Error:" in result.output

    def test_cli_invalid_try_format(self):
        """Test CLI with invalid try format."""
        runner = CliRunner()
        result = runner.invoke(cli, ["try", "invalid format"])
        assert result.exit_code == 2
        assert "Error:" in result.output

    def test_cli_invalid_horoscope_format(self):
        """Test CLI with invalid horoscope format."""
        runner = CliRunner()
        result = runner.invoke(cli, ["horoscope", "invalid format"])
        assert result.exit_code == 1
        assert "Error:" in result.output


class TestCLIUtilities:
    def test_get_place_coordinates_valid(self):
        """Test get_place_coordinates with valid place."""
        coords = get_place_coordinates("delhi")
        assert coords is not None
        lat, lon, name = coords
        assert lat == 28.6
        assert lon == 77.2
        assert name == "Delhi"

    def test_get_place_coordinates_invalid(self):
        """Test get_place_coordinates with invalid place."""
        coords = get_place_coordinates("nonexistent")
        assert coords is None

    def test_get_place_coordinates_case_insensitive(self):
        """Test get_place_coordinates is case insensitive."""
        coords1 = get_place_coordinates("DELHI")
        coords2 = get_place_coordinates("delhi")
        assert coords1 == coords2

    def test_validate_date_valid(self):
        """Test validate_date with valid date."""
        validate_date(2024, 3, 15)  # Should not raise

    def test_validate_date_invalid_year(self):
        """Test validate_date with invalid year."""
        with pytest.raises(click.BadParameter):
            validate_date(5000, 3, 15)

    def test_validate_date_invalid_month(self):
        """Test validate_date with invalid month."""
        with pytest.raises(click.BadParameter):
            validate_date(2024, 13, 15)

    def test_validate_date_invalid_day(self):
        """Test validate_date with invalid day."""
        with pytest.raises(click.BadParameter):
            validate_date(2024, 3, 32)

    def test_validate_time_valid(self):
        """Test validate_time with valid time."""
        validate_time(14, 30)  # Should not raise

    def test_validate_time_invalid_hour(self):
        """Test validate_time with invalid hour."""
        with pytest.raises(click.BadParameter):
            validate_time(25, 30)

    def test_validate_time_invalid_minute(self):
        """Test validate_time with invalid minute."""
        with pytest.raises(click.BadParameter):
            validate_time(14, 60)

    def test_parse_date_string_valid(self):
        """Test parse_date_string with valid date."""
        year, month, day = parse_date_string("2024 3 15")
        assert year == 2024
        assert month == 3
        assert day == 15

    def test_parse_date_string_invalid_format(self):
        """Test parse_date_string with invalid format."""
        with pytest.raises(click.BadParameter):
            parse_date_string("2024-3-15")

    def test_parse_date_string_invalid_parts(self):
        """Test parse_date_string with wrong number of parts."""
        with pytest.raises(click.BadParameter):
            parse_date_string("2024 3")

    def test_parse_try_string_valid(self):
        """Test parse_try_string with valid format."""
        year_system, year, month, paksa, tithi = parse_try_string("saka 1946 2 sukla 5")
        assert year_system == "saka"
        assert year == 1946
        assert month == 2
        assert paksa == "sukla"
        assert tithi == 5

    def test_parse_try_string_invalid_format(self):
        """Test parse_try_string with invalid format."""
        with pytest.raises(click.BadParameter):
            parse_try_string("saka 1946 2 sukla")

    def test_parse_try_string_invalid_year_system(self):
        """Test parse_try_string with invalid year system."""
        with pytest.raises(click.BadParameter):
            parse_try_string("invalid 1946 2 sukla 5")

    def test_parse_try_string_invalid_paksa(self):
        """Test parse_try_string with invalid paksa."""
        with pytest.raises(click.BadParameter):
            parse_try_string("saka 1946 2 invalid 5")

    def test_parse_try_string_invalid_month(self):
        """Test parse_try_string with invalid month."""
        with pytest.raises(click.BadParameter):
            parse_try_string("saka 1946 12 sukla 5")

    def test_parse_try_string_invalid_tithi(self):
        """Test parse_try_string with invalid tithi."""
        with pytest.raises(click.BadParameter):
            parse_try_string("saka 1946 2 sukla 16")

    def test_edge_cases(self):
        """Test edge cases."""
        # Test with minimum valid values
        validate_date(-4712, 1, 1)
        validate_time(0, 0)

        # Test with maximum valid values
        validate_date(3000, 12, 31)
        validate_time(23, 59)

        # Test parse with edge values
        year, month, day = parse_date_string("-4712 1 1")
        assert year == -4712
        assert month == 1
        assert day == 1

    def test_consistency_checks(self):
        """Test consistency between related functions."""
        # Test that place coordinates are consistent
        coords = get_place_coordinates("ujjain")
        assert coords is not None
        lat, lon, name = coords
        assert lat == 23.2
        assert lon == 75.8
        assert name == "Ujjain"

        # Test that date parsing is consistent
        year, month, day = parse_date_string("2024 3 15")
        validate_date(year, month, day)  # Should not raise

        # Test that try parsing is consistent
        year_system, year, month, paksa, tithi = parse_try_string("saka 1946 2 sukla 5")
        assert year_system in ["saka", "vikrama"]
        assert paksa in ["sukla", "krsna"]
        assert 0 <= month <= 11
        assert 1 <= tithi <= 15
