"""
Tests for MarkiTect cost tracking CLI commands.

This module tests the command-line interface for cost management including:
- Cost report generation commands
- Cost item management commands
- Category management commands
- Period cost calculations
"""

import pytest
import tempfile
import os
import json
from datetime import date
from decimal import Decimal
from click.testing import CliRunner

from markitect.finance.cli import cost_commands
from markitect.finance.cost_manager import CostItemManager, CostItem
from markitect.finance.models import FinanceModels


class TestCostCLICommands:
    """Test suite for cost tracking CLI commands."""

    @pytest.fixture
    def temp_db(self):
        """Create temporary database for testing."""
        fd, path = tempfile.mkstemp(suffix='.db')
        os.close(fd)
        yield path
        os.unlink(path)

    @pytest.fixture
    def setup_test_data(self, temp_db):
        """Setup test database with sample cost data."""
        finance_models = FinanceModels(temp_db)
        finance_models.initialize_finance_schema()

        cost_manager = CostItemManager(temp_db)

        # Get categories
        infra_cat = cost_manager.get_category_by_name('Infrastructure')
        software_cat = cost_manager.get_category_by_name('Software')

        # Create sample cost items
        cost_items = [
            CostItem(
                category_id=infra_cat['id'],
                name='Test Server',
                cost_type='monthly',
                amount_eur=Decimal('25.00'),
                starting_from_date=date(2025, 1, 1)
            ),
            CostItem(
                category_id=software_cat['id'],
                name='Test Software',
                cost_type='one_time',
                amount_eur=Decimal('50.00'),
                starting_from_date=date(2025, 1, 15)
            )
        ]

        for item in cost_items:
            cost_manager.create_cost_item(item)

        return temp_db

    @pytest.fixture
    def runner(self):
        """Create Click test runner."""
        return CliRunner()

    def test_cost_report_generate_summary(self, runner, setup_test_data):
        """Test cost report generate command with summary format."""
        result = runner.invoke(cost_commands, [
            'report', 'generate',
            '--period', '2025-01',
            '--format', 'summary',
            '--database', setup_test_data
        ])

        assert result.exit_code == 0
        assert "Cost Summary Report - January 2025" in result.output
        assert "€75.00" in result.output  # 25 + 50
        assert "frontmatter" not in result.output.lower()  # Should be properly formatted

    def test_cost_report_generate_detailed(self, runner, setup_test_data):
        """Test cost report generate command with detailed format."""
        result = runner.invoke(cost_commands, [
            'report', 'generate',
            '--period', '2025-01',
            '--format', 'detailed',
            '--database', setup_test_data
        ])

        assert result.exit_code == 0
        assert "Detailed Cost Report - January 2025" in result.output
        assert "Infrastructure" in result.output
        assert "Software" in result.output
        assert "Test Server" in result.output
        assert "Test Software" in result.output

    def test_cost_report_generate_audit(self, runner, setup_test_data):
        """Test cost report generate command with audit format."""
        result = runner.invoke(cost_commands, [
            'report', 'generate',
            '--period', '2025-01',
            '--format', 'audit',
            '--database', setup_test_data
        ])

        assert result.exit_code == 0
        assert "Cost Audit Report - January 2025" in result.output
        assert "Audit Summary" in result.output
        assert "Transaction History" in result.output

    def test_cost_report_generate_with_output_file(self, runner, setup_test_data):
        """Test saving report to output file."""
        with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.md') as f:
            output_path = f.name

        try:
            result = runner.invoke(cost_commands, [
                'report', 'generate',
                '--period', '2025-01',
                '--output', output_path,
                '--database', setup_test_data
            ])

            assert result.exit_code == 0
            assert f"Report saved to: {output_path}" in result.output

            # Verify file was created
            assert os.path.exists(output_path)
            with open(output_path, 'r') as f:
                content = f.read()
                assert "Cost Summary Report" in content

        finally:
            if os.path.exists(output_path):
                os.unlink(output_path)

    def test_cost_report_generate_invalid_period(self, runner, setup_test_data):
        """Test report generation with invalid period format."""
        result = runner.invoke(cost_commands, [
            'report', 'generate',
            '--period', 'invalid-period',
            '--database', setup_test_data
        ])

        assert result.exit_code == 1
        assert "Period must be in YYYY-MM format" in result.output

    def test_cost_report_generate_default_database(self, runner):
        """Test report generation with default database path from config."""
        result = runner.invoke(cost_commands, [
            'report', 'generate',
            '--period', '2025-01'
        ])

        # Should succeed with default config and empty database
        assert result.exit_code == 0
        assert "Cost Summary Report - January 2025" in result.output
        assert "€0.00" in result.output  # Empty database shows zero costs

    def test_cost_report_template_show(self, runner):
        """Test cost report template show command."""
        result = runner.invoke(cost_commands, [
            'report', 'template', '--show'
        ])

        assert result.exit_code == 0
        assert "Summary Report Template" in result.output
        assert "Description" in result.output
        assert "Frontmatter Fields" in result.output

    def test_cost_report_template_different_formats(self, runner):
        """Test template show for different formats."""
        formats = ['summary', 'detailed', 'audit']

        for format_type in formats:
            result = runner.invoke(cost_commands, [
                'report', 'template', '--show', '--format', format_type
            ])

            assert result.exit_code == 0
            assert f"{format_type.title()} Report Template" in result.output

    def test_cost_item_add(self, runner, temp_db):
        """Test adding new cost item via CLI."""
        # Initialize database
        finance_models = FinanceModels(temp_db)
        finance_models.initialize_finance_schema()

        result = runner.invoke(cost_commands, [
            'item', 'add', 'Test Item',
            '--category', 'Infrastructure',
            '--amount', '15.50',
            '--type', 'monthly',
            '--database', temp_db
        ])

        assert result.exit_code == 0
        assert "✅ Created cost item 'Test Item'" in result.output

        # Verify item was created
        cost_manager = CostItemManager(temp_db)
        items = cost_manager.list_cost_items()
        assert len(items) == 1
        assert items[0]['name'] == 'Test Item'
        assert float(items[0]['amount_eur']) == 15.50

    def test_cost_item_add_with_description_and_date(self, runner, temp_db):
        """Test adding cost item with description and start date."""
        finance_models = FinanceModels(temp_db)
        finance_models.initialize_finance_schema()

        result = runner.invoke(cost_commands, [
            'item', 'add', 'Test Item',
            '--category', 'Software',
            '--amount', '99.99',
            '--type', 'one_time',
            '--description', 'Test description',
            '--start-date', '2025-01-15',
            '--database', temp_db
        ])

        assert result.exit_code == 0
        assert "✅ Created cost item 'Test Item'" in result.output

    def test_cost_item_add_invalid_category(self, runner, temp_db):
        """Test adding item with non-existent category."""
        finance_models = FinanceModels(temp_db)
        finance_models.initialize_finance_schema()

        result = runner.invoke(cost_commands, [
            'item', 'add', 'Test Item',
            '--category', 'NonExistent',
            '--amount', '10.00',
            '--type', 'monthly',
            '--database', temp_db
        ])

        assert result.exit_code == 1
        assert "Category 'NonExistent' not found" in result.output
        assert "Available categories:" in result.output

    def test_cost_item_list(self, runner, setup_test_data):
        """Test listing cost items."""
        result = runner.invoke(cost_commands, [
            'item', 'list',
            '--database', setup_test_data
        ])

        assert result.exit_code == 0
        assert "Test Server" in result.output
        assert "Test Software" in result.output
        assert "€25.00" in result.output
        assert "€50.00" in result.output

    def test_cost_item_list_with_filters(self, runner, setup_test_data):
        """Test listing cost items with filters."""
        # Filter by category
        result = runner.invoke(cost_commands, [
            'item', 'list',
            '--category', 'Infrastructure',
            '--database', setup_test_data
        ])

        assert result.exit_code == 0
        assert "Test Server" in result.output
        assert "Test Software" not in result.output

        # Filter by type
        result = runner.invoke(cost_commands, [
            'item', 'list',
            '--type', 'monthly',
            '--database', setup_test_data
        ])

        assert result.exit_code == 0
        assert "Test Server" in result.output
        assert "Test Software" not in result.output

    def test_cost_category_list(self, runner, setup_test_data):
        """Test listing cost categories."""
        result = runner.invoke(cost_commands, [
            'category', 'list',
            '--database', setup_test_data
        ])

        assert result.exit_code == 0
        assert "Infrastructure" in result.output
        assert "Software" in result.output
        assert "Total: 8 categories" in result.output  # Default categories

    def test_cost_category_add(self, runner, temp_db):
        """Test adding new cost category."""
        finance_models = FinanceModels(temp_db)
        finance_models.initialize_finance_schema()

        result = runner.invoke(cost_commands, [
            'category', 'add', 'Custom Category',
            '--description', 'Custom test category',
            '--database', temp_db
        ])

        assert result.exit_code == 0
        assert "✅ Created category 'Custom Category'" in result.output

        # Verify category was created
        cost_manager = CostItemManager(temp_db)
        categories = cost_manager.list_categories()
        category_names = [cat['name'] for cat in categories]
        assert 'Custom Category' in category_names

    def test_cost_calculate(self, runner, setup_test_data):
        """Test cost calculation command."""
        result = runner.invoke(cost_commands, [
            'calculate',
            '--period', '2025-01',
            '--database', setup_test_data
        ])

        assert result.exit_code == 0
        assert "Cost Calculation - January 2025" in result.output
        assert "Monthly Recurring: €25.00" in result.output
        assert "One-time Expenses: €50.00" in result.output
        assert "Total Period Cost: €75.00" in result.output
        assert "Active Cost Items: 2" in result.output

    def test_cost_calculate_current_month(self, runner, setup_test_data):
        """Test cost calculation for current month (default)."""
        result = runner.invoke(cost_commands, [
            'calculate',
            '--database', setup_test_data
        ])

        assert result.exit_code == 0
        assert "Cost Calculation" in result.output
        # Should default to current month

    def test_cost_calculate_invalid_period(self, runner, setup_test_data):
        """Test cost calculation with invalid period."""
        result = runner.invoke(cost_commands, [
            'calculate',
            '--period', 'invalid',
            '--database', setup_test_data
        ])

        assert result.exit_code == 1
        assert "Period must be in YYYY-MM format" in result.output

    def test_cost_item_add_invalid_date_format(self, runner, temp_db):
        """Test adding item with invalid date format."""
        finance_models = FinanceModels(temp_db)
        finance_models.initialize_finance_schema()

        result = runner.invoke(cost_commands, [
            'item', 'add', 'Test Item',
            '--category', 'Infrastructure',
            '--amount', '10.00',
            '--type', 'monthly',
            '--start-date', 'invalid-date',
            '--database', temp_db
        ])

        assert result.exit_code == 1
        assert "Start date must be in YYYY-MM-DD format" in result.output

    def test_help_commands(self, runner):
        """Test help output for cost commands."""
        # Test main cost help
        result = runner.invoke(cost_commands, ['--help'])
        assert result.exit_code == 0
        assert "Cost tracking and financial reporting commands" in result.output

        # Test report help
        result = runner.invoke(cost_commands, ['report', '--help'])
        assert result.exit_code == 0
        assert "Generate cost reports" in result.output

        # Test item help
        result = runner.invoke(cost_commands, ['item', '--help'])
        assert result.exit_code == 0
        assert "Manage cost items" in result.output

        # Test category help
        result = runner.invoke(cost_commands, ['category', '--help'])
        assert result.exit_code == 0
        assert "Manage cost categories" in result.output