"""
Tests for Issue #124 - Single command Day-Wrap-Up

This module contains comprehensive tests for the day wrap-up functionality
that consolidates daily work summaries, activity tracking, cost distribution,
and reporting into a single convenient command.
"""

import pytest
import tempfile
from datetime import datetime, date, timedelta
from unittest.mock import Mock, patch, MagicMock
from pathlib import Path
from decimal import Decimal
import json

from markitect.finance.day_wrapup_commands import DayWrapUpService, wrapup, _display_daily_summary, _display_period_summary


class TestDayWrapUpService:
    """Test suite for DayWrapUpService."""

    def setup_method(self):
        """Set up test fixtures with temporary database."""
        self.temp_db = tempfile.NamedTemporaryFile(suffix='.db', delete=False)
        self.temp_db.close()
        self.db_path = self.temp_db.name
        self.service = DayWrapUpService(self.db_path)

    def teardown_method(self):
        """Clean up test fixtures."""
        Path(self.db_path).unlink(missing_ok=True)

    def test_service_initialization(self):
        """Test that service initializes properly with all trackers."""
        assert self.service.db_path == self.db_path
        assert self.service.worktime_tracker is not None
        assert self.service.activity_tracker is not None
        assert self.service.session_tracker is not None

    def test_get_worktime_summary_no_data(self):
        """Test worktime summary when no data exists."""
        today = date.today()
        summary = self.service._get_worktime_summary(today)

        assert summary['total_minutes'] == 0
        assert summary['total_hours'] == 0.0
        assert summary['issues_worked'] == 0
        assert summary['entries'] == []
        assert summary['cost_allocated'] is None
        assert summary['cost_per_minute'] is None

    def test_get_worktime_summary_with_data(self):
        """Test worktime summary with logged data."""
        today = date.today()

        # Log some worktime
        self.service.worktime_tracker.log_worktime(124, 90, work_date=today, description="Main work")
        self.service.worktime_tracker.log_worktime(125, 60, work_date=today, description="Side work")

        summary = self.service._get_worktime_summary(today)

        assert summary['total_minutes'] == 150  # 90 + 60
        assert summary['total_hours'] == 2.5
        assert summary['issues_worked'] == 2
        assert summary['entries'] == 2
        assert len(summary['issue_breakdown']) == 2
        assert 124 in summary['issue_breakdown']
        assert 125 in summary['issue_breakdown']
        assert summary['issue_breakdown'][124]['minutes'] == 90
        assert summary['issue_breakdown'][125]['minutes'] == 60

    def test_get_activity_summary_no_data(self):
        """Test activity summary when no data exists."""
        today = date.today()
        summary = self.service._get_activity_summary(today)

        assert summary['total_activities'] == 0
        assert summary['unique_issues'] == 0
        assert summary['activities_by_type'] == {}
        assert summary['activities'] == []

    def test_get_activity_summary_with_data(self):
        """Test activity summary with logged data."""
        today = date.today()

        # Log some activities
        from markitect.issues.activity_tracker import ActivityType
        self.service.activity_tracker.log_activity(124, ActivityType.CREATED, activity_date=today, activity_details="Created issue")
        self.service.activity_tracker.log_activity(124, ActivityType.MODIFIED, activity_date=today, activity_details="Updated issue")
        self.service.activity_tracker.log_activity(125, ActivityType.CREATED, activity_date=today, activity_details="Created another")

        summary = self.service._get_activity_summary(today)

        assert summary['total_activities'] == 3
        assert summary['unique_issues'] == 2
        assert 'created' in summary['activities_by_type']
        assert 'modified' in summary['activities_by_type']
        assert summary['activities_by_type']['created'] == 2
        assert summary['activities_by_type']['modified'] == 1
        assert len(summary['activities']) == 3

    def test_get_cost_summary_no_distribution(self):
        """Test cost summary when no cost distribution exists."""
        today = date.today()
        summary = self.service._get_cost_summary(today)

        assert summary['daily_total'] == 0.0
        assert summary['issue_costs'] == {}
        assert summary['has_cost_allocation'] is False

    def test_get_cost_summary_with_distribution(self):
        """Test cost summary with cost distribution data."""
        today = date.today()

        # Log worktime and distribute costs
        self.service.worktime_tracker.log_worktime(124, 120, work_date=today)  # 2 hours
        self.service.worktime_tracker.log_worktime(125, 60, work_date=today)   # 1 hour

        distribution = self.service.worktime_tracker.distribute_daily_costs(
            work_date=today,
            total_daily_cost=Decimal('90.00')  # €90 total
        )

        summary = self.service._get_cost_summary(today)

        assert summary['daily_total'] == 90.0
        assert summary['has_cost_allocation'] is True
        assert len(summary['issue_costs']) == 2
        assert summary['issue_costs'][124] == 60.0  # 2/3 of €90
        assert summary['issue_costs'][125] == 30.0  # 1/3 of €90

    def test_generate_recommendations_no_data(self):
        """Test recommendation generation with no data."""
        summary = {
            'worktime': {'total_minutes': 0, 'total_hours': 0.0, 'issues_worked': 0},
            'activities': {'total_activities': 0, 'unique_issues': 0},
            'costs': {'has_cost_allocation': False}
        }

        recommendations = self.service._generate_recommendations(summary)

        assert len(recommendations) >= 2
        assert any("No worktime logged" in rec for rec in recommendations)
        assert any("No issue activities logged" in rec for rec in recommendations)

    def test_generate_recommendations_with_data(self):
        """Test recommendation generation with various data conditions."""
        # Test low worktime
        summary = {
            'worktime': {'total_minutes': 120, 'total_hours': 2.0, 'issues_worked': 1},
            'activities': {'total_activities': 5, 'unique_issues': 1},
            'costs': {'has_cost_allocation': False}
        }

        recommendations = self.service._generate_recommendations(summary)
        assert any("Low worktime logged" in rec for rec in recommendations)
        assert any("no costs distributed" in rec for rec in recommendations)

        # Test high worktime
        summary['worktime'] = {'total_minutes': 660, 'total_hours': 11.0, 'issues_worked': 1}
        recommendations = self.service._generate_recommendations(summary)
        assert any("High worktime logged" in rec for rec in recommendations)

        # Test many issues
        summary['activities'] = {'total_activities': 10, 'unique_issues': 6}
        recommendations = self.service._generate_recommendations(summary)
        assert any("Many issues worked on" in rec for rec in recommendations)

    def test_perform_auto_estimation_no_activities(self):
        """Test auto estimation when no activities exist."""
        today = date.today()

        result = self.service.perform_auto_estimation(today, 8.0)

        assert result['estimated'] is False
        assert "No active issues found" in result['reason']
        assert result['active_issues'] == []

    def test_perform_auto_estimation_with_existing_time(self):
        """Test auto estimation when time is already logged."""
        today = date.today()

        # Log some worktime first
        self.service.worktime_tracker.log_worktime(124, 60, work_date=today)

        result = self.service.perform_auto_estimation(today, 8.0)

        assert result['estimated'] is False
        assert "Time already logged" in result['reason']
        assert result['existing_minutes'] == 60

    def test_perform_auto_estimation_success(self):
        """Test successful auto estimation."""
        today = date.today()

        # Create activities for issues
        from markitect.issues.activity_tracker import ActivityType
        self.service.activity_tracker.log_activity(124, ActivityType.CREATED, activity_date=today)
        self.service.activity_tracker.log_activity(125, ActivityType.MODIFIED, activity_date=today)

        result = self.service.perform_auto_estimation(today, 6.0)

        assert result['estimated'] is True
        assert 'estimation_result' in result
        estimation = result['estimation_result']
        assert estimation['total_minutes'] == 360  # 6 hours
        assert estimation['issues_count'] == 2
        assert len(estimation['issue_estimates']) == 2

        # Verify worktime entries were created
        entries = self.service.worktime_tracker.get_worktime_entries(work_date=today)
        assert len(entries) == 2
        assert all(e.entry_type == "estimated" for e in entries)

    def test_distribute_daily_costs(self):
        """Test daily cost distribution functionality."""
        today = date.today()

        # Log worktime first
        self.service.worktime_tracker.log_worktime(124, 180, work_date=today)  # 3 hours
        self.service.worktime_tracker.log_worktime(125, 120, work_date=today)  # 2 hours
        # Total: 5 hours (300 minutes)

        result = self.service.distribute_daily_costs(today, Decimal('150.00'))

        assert result['total_cost'] == 150.0
        assert result['total_minutes'] == 300
        assert result['cost_per_minute'] == 0.5
        assert result['distributions'][124]['cost_allocated'] == 90.0  # 3/5 * €150
        assert result['distributions'][125]['cost_allocated'] == 60.0  # 2/5 * €150

    def test_generate_daily_summary_integration(self):
        """Test complete daily summary generation."""
        today = date.today()

        # Create comprehensive test data
        from markitect.issues.activity_tracker import ActivityType

        # Log worktime
        self.service.worktime_tracker.log_worktime(124, 120, work_date=today, description="Main feature")
        self.service.worktime_tracker.log_worktime(125, 60, work_date=today, description="Bug fix")

        # Log activities
        self.service.activity_tracker.log_activity(124, ActivityType.CREATED, activity_date=today)
        self.service.activity_tracker.log_activity(124, ActivityType.MODIFIED, activity_date=today)
        self.service.activity_tracker.log_activity(125, ActivityType.CLOSED, activity_date=today)

        # Distribute costs
        self.service.distribute_daily_costs(today, Decimal('90.00'))

        # Generate summary
        summary = self.service.generate_daily_summary(today)

        # Verify summary structure
        assert summary['date'] == today
        assert 'worktime' in summary
        assert 'activities' in summary
        assert 'costs' in summary
        assert 'recommendations' in summary

        # Verify worktime data
        worktime = summary['worktime']
        assert worktime['total_minutes'] == 180
        assert worktime['total_hours'] == 3.0
        assert worktime['issues_worked'] == 2
        assert worktime['cost_allocated'] == 90.0

        # Verify activity data
        activities = summary['activities']
        assert activities['total_activities'] == 3
        assert activities['unique_issues'] == 2

        # Verify cost data
        costs = summary['costs']
        assert costs['daily_total'] == 90.0
        assert costs['has_cost_allocation'] is True

        # Verify recommendations exist
        assert isinstance(summary['recommendations'], list)


class TestDayWrapUpCommands:
    """Test suite for day wrap-up CLI commands."""

    @patch('markitect.finance.day_wrapup_commands.DayWrapUpService')
    def test_daily_command_basic(self, mock_service_class):
        """Test the daily wrap-up command with basic functionality."""
        mock_service = Mock()
        mock_service_class.return_value = mock_service

        # Mock the summary data
        mock_summary = {
            'date': date.today(),
            'worktime': {
                'total_minutes': 180,
                'total_hours': 3.0,
                'issues_worked': 2,
                'entries': 2,
                'issue_breakdown': {124: {'minutes': 120, 'entries': 1}, 125: {'minutes': 60, 'entries': 1}},
                'cost_allocated': 90.0,
                'cost_per_minute': 0.5
            },
            'activities': {
                'total_activities': 3,
                'unique_issues': 2,
                'activities_by_type': {'created': 2, 'modified': 1},
                'activities': []
            },
            'costs': {
                'daily_total': 90.0,
                'issue_costs': {124: 60.0, 125: 30.0},
                'has_cost_allocation': True
            },
            'recommendations': ["💰 Costs distributed successfully"]
        }
        mock_service.generate_daily_summary.return_value = mock_summary

        from click.testing import CliRunner
        runner = CliRunner()

        result = runner.invoke(wrapup, ['daily'])

        assert result.exit_code == 0
        assert "📊 Daily Wrap-Up" in result.output
        assert "⏰ WORKTIME SUMMARY" in result.output
        assert "📝 ACTIVITIES SUMMARY" in result.output
        assert "💰 COST SUMMARY" in result.output
        assert "💡 RECOMMENDATIONS" in result.output

    @patch('markitect.finance.day_wrapup_commands.DayWrapUpService')
    def test_daily_command_with_auto_estimate(self, mock_service_class):
        """Test daily command with auto-estimation enabled."""
        mock_service = Mock()
        mock_service_class.return_value = mock_service

        # Mock estimation result
        mock_estimation = {
            'estimated': True,
            'estimation_result': {
                'total_minutes': 480,
                'issues_count': 2,
                'issue_estimates': {124: 240, 125: 240}
            }
        }
        mock_service.perform_auto_estimation.return_value = mock_estimation

        # Mock summary
        mock_service.generate_daily_summary.return_value = {
            'date': date.today(),
            'worktime': {'total_minutes': 0, 'total_hours': 0.0, 'issues_worked': 0, 'entries': []},
            'activities': {'total_activities': 0, 'unique_issues': 0, 'activities_by_type': {}, 'activities': []},
            'costs': {'daily_total': 0.0, 'issue_costs': {}, 'has_cost_allocation': False},
            'recommendations': []
        }

        from click.testing import CliRunner
        runner = CliRunner()

        result = runner.invoke(wrapup, ['daily', '--auto-estimate', '--estimate-hours', '8'])

        assert result.exit_code == 0
        assert "🤖 Auto-estimating worktime" in result.output
        assert "✅ Estimated 8.0h across 2 issues" in result.output
        mock_service.perform_auto_estimation.assert_called_once_with(date.today(), 8.0)

    @patch('markitect.finance.day_wrapup_commands.DayWrapUpService')
    def test_daily_command_with_cost_distribution(self, mock_service_class):
        """Test daily command with cost distribution."""
        mock_service = Mock()
        mock_service_class.return_value = mock_service

        # Mock distribution result
        mock_distribution = {
            'total_cost': 120.0,
            'total_minutes': 240,
            'issues_count': 2,
            'distributions': {124: {'cost_allocated': 80.0}, 125: {'cost_allocated': 40.0}}
        }
        mock_service.distribute_daily_costs.return_value = mock_distribution

        # Mock summary
        mock_service.generate_daily_summary.return_value = {
            'date': date.today(),
            'worktime': {'total_minutes': 0, 'total_hours': 0.0, 'issues_worked': 0, 'entries': []},
            'activities': {'total_activities': 0, 'unique_issues': 0, 'activities_by_type': {}, 'activities': []},
            'costs': {'daily_total': 0.0, 'issue_costs': {}, 'has_cost_allocation': False},
            'recommendations': []
        }

        from click.testing import CliRunner
        runner = CliRunner()

        result = runner.invoke(wrapup, ['daily', '--distribute-cost', '120'])

        assert result.exit_code == 0
        assert "💰 Distributing €120.00" in result.output
        assert "✅ Distributed €120.00 across 2 issues" in result.output
        mock_service.distribute_daily_costs.assert_called_once()

    @patch('markitect.finance.day_wrapup_commands.DayWrapUpService')
    def test_daily_command_json_format(self, mock_service_class):
        """Test daily command with JSON output format."""
        mock_service = Mock()
        mock_service_class.return_value = mock_service

        mock_summary = {
            'date': date.today(),
            'worktime': {'total_minutes': 120, 'total_hours': 2.0, 'issues_worked': 1, 'entries': 1},
            'activities': {'total_activities': 2, 'unique_issues': 1, 'activities_by_type': {}, 'activities': []},
            'costs': {'daily_total': 0.0, 'issue_costs': {}, 'has_cost_allocation': False},
            'recommendations': []
        }
        mock_service.generate_daily_summary.return_value = mock_summary

        from click.testing import CliRunner
        runner = CliRunner()

        result = runner.invoke(wrapup, ['daily', '--format', 'json'])

        assert result.exit_code == 0
        # Should be valid JSON
        output_data = json.loads(result.output.strip())
        assert 'date' in output_data
        assert 'worktime' in output_data
        assert 'activities' in output_data
        assert 'costs' in output_data

    @patch('markitect.finance.day_wrapup_commands.DayWrapUpService')
    def test_estimate_command(self, mock_service_class):
        """Test the estimate command."""
        mock_service = Mock()
        mock_service_class.return_value = mock_service

        mock_estimation = {
            'estimated': True,
            'estimation_result': {
                'work_date': date.today(),
                'total_minutes': 480,  # 8 hours
                'distribution_method': 'activity_based',
                'issue_estimates': {124: 300, 125: 180},  # 5h and 3h
                'issues_count': 2
            }
        }
        mock_service.perform_auto_estimation.return_value = mock_estimation

        from click.testing import CliRunner
        runner = CliRunner()

        today = date.today().strftime('%Y-%m-%d')
        result = runner.invoke(wrapup, ['estimate', today, '--hours', '8'])

        assert result.exit_code == 0
        assert "✅ Estimated worktime" in result.output
        assert "Total Hours: 8.0h" in result.output
        assert "Issues: 2" in result.output
        assert "Estimated Time Distribution:" in result.output

    @patch('markitect.finance.day_wrapup_commands.DayWrapUpService')
    def test_period_command(self, mock_service_class):
        """Test the period wrap-up command."""
        mock_service = Mock()
        mock_service_class.return_value = mock_service

        # Mock worktime report
        mock_worktime_report = {
            'period': '2025-10-01 to 2025-10-04',
            'total_entries': 8,
            'total_time': {'hours': 20, 'minutes': 30, 'total_minutes': 1230},
            'unique_issues': 3,
            'unique_dates': 4,
            'average_minutes_per_day': 307.5
        }
        mock_service.worktime_tracker.get_worktime_report.return_value = mock_worktime_report

        # Mock activity summary
        mock_activity_summary = {
            'total_activities': 15,
            'unique_issues': 4,
            'activities_by_type': {'created': 8, 'modified': 5, 'closed': 2}
        }
        mock_service.activity_tracker.get_activity_summary.return_value = mock_activity_summary

        from click.testing import CliRunner
        runner = CliRunner()

        result = runner.invoke(wrapup, ['period', '2025-10-01', '2025-10-04'])

        assert result.exit_code == 0
        assert "📈 Period Wrap-Up" in result.output
        assert "⏰ WORKTIME OVERVIEW" in result.output
        assert "📝 ACTIVITIES OVERVIEW" in result.output
        assert "Total Time: 20h 30m" in result.output
        assert "Total Activities: 15" in result.output


class TestDayWrapUpIntegration:
    """Integration tests for the complete day wrap-up system."""

    def setup_method(self):
        """Set up integration test fixtures."""
        self.temp_db = tempfile.NamedTemporaryFile(suffix='.db', delete=False)
        self.temp_db.close()
        self.db_path = self.temp_db.name

    def teardown_method(self):
        """Clean up integration test fixtures."""
        Path(self.db_path).unlink(missing_ok=True)

    def test_complete_day_workflow(self):
        """Test a complete daily workflow from start to finish."""
        service = DayWrapUpService(self.db_path)
        today = date.today()

        # 1. Start with empty day
        initial_summary = service.generate_daily_summary(today)
        assert initial_summary['worktime']['total_minutes'] == 0
        assert initial_summary['activities']['total_activities'] == 0

        # 2. Log some activities
        from markitect.issues.activity_tracker import ActivityType
        service.activity_tracker.log_activity(124, ActivityType.CREATED, activity_date=today, activity_details="Started new feature")
        service.activity_tracker.log_activity(124, ActivityType.MODIFIED, activity_date=today, activity_details="Made progress")
        service.activity_tracker.log_activity(125, ActivityType.CLOSED, activity_date=today, activity_details="Fixed bug")

        # 3. Perform auto-estimation
        estimation = service.perform_auto_estimation(today, 7.5)
        assert estimation['estimated'] is True
        assert estimation['estimation_result']['total_minutes'] == 450  # 7.5 hours

        # 4. Distribute costs
        distribution = service.distribute_daily_costs(today, Decimal('112.50'))  # €15 per hour
        assert distribution['total_cost'] == 112.5
        assert distribution['cost_per_minute'] == 0.25  # €0.25 per minute

        # 5. Generate final summary
        final_summary = service.generate_daily_summary(today)

        # Verify complete summary
        assert final_summary['worktime']['total_hours'] == 7.5
        assert final_summary['worktime']['issues_worked'] == 2
        assert final_summary['worktime']['cost_allocated'] == 112.5

        assert final_summary['activities']['total_activities'] == 3
        assert final_summary['activities']['unique_issues'] == 2

        assert final_summary['costs']['daily_total'] == 112.5
        assert final_summary['costs']['has_cost_allocation'] is True
        assert len(final_summary['costs']['issue_costs']) == 2

        # Verify recommendations are helpful
        recommendations = final_summary['recommendations']
        assert len(recommendations) >= 0  # Should have reasonable recommendations

    def test_multi_day_period_summary(self):
        """Test period summary across multiple days."""
        service = DayWrapUpService(self.db_path)

        # Create data across multiple days
        dates = [date.today() - timedelta(days=i) for i in range(3)]  # Last 3 days

        for i, test_date in enumerate(dates):
            # Log different amounts of work each day
            hours = 6 + i * 2  # 6, 8, 10 hours
            minutes = hours * 60

            service.worktime_tracker.log_worktime(124, minutes // 2, work_date=test_date, description=f"Day {i+1} main work")
            service.worktime_tracker.log_worktime(125 + i, minutes // 2, work_date=test_date, description=f"Day {i+1} side work")

            # Log activities
            from markitect.issues.activity_tracker import ActivityType
            service.activity_tracker.log_activity(124, ActivityType.CREATED, activity_date=test_date)
            service.activity_tracker.log_activity(125 + i, ActivityType.MODIFIED, activity_date=test_date)

        # Generate period report
        start_date = dates[-1]  # Oldest date
        end_date = dates[0]     # Most recent date

        worktime_report = service.worktime_tracker.get_worktime_report(
            start_date=start_date,
            end_date=end_date
        )

        # Verify period data
        assert worktime_report['total_entries'] == 6  # 2 entries per day * 3 days
        assert worktime_report['total_time']['total_minutes'] == 1440  # 6+8+10 = 24 hours
        assert worktime_report['unique_issues'] == 4  # Issues 124, 125, 126, 127
        assert worktime_report['unique_dates'] == 3

        # Verify daily averages
        expected_avg = 1440 / 3  # 480 minutes per day on average
        assert abs(worktime_report['average_minutes_per_day'] - expected_avg) < 1

    def test_error_handling_and_edge_cases(self):
        """Test error handling and edge cases."""
        service = DayWrapUpService(self.db_path)
        today = date.today()

        # Test estimation with no activities
        estimation = service.perform_auto_estimation(today, 8.0)
        assert estimation['estimated'] is False
        assert "No active issues found" in estimation['reason']

        # Test cost distribution with no worktime
        distribution = service.distribute_daily_costs(today, Decimal('100.00'))
        assert 'message' in distribution
        assert "No worktime entries found" in distribution['message']

        # Test summary generation with partial data
        from markitect.issues.activity_tracker import ActivityType
        service.activity_tracker.log_activity(124, ActivityType.CREATED, activity_date=today)

        summary = service.generate_daily_summary(today)
        assert summary['worktime']['total_minutes'] == 0  # No worktime logged
        assert summary['activities']['total_activities'] == 1  # But activity exists
        assert "No worktime logged" in ' '.join(summary['recommendations'])

        # Test recommendations for edge cases
        service.worktime_tracker.log_worktime(124, 720, work_date=today)  # 12 hours - excessive
        summary = service.generate_daily_summary(today)
        assert any("High worktime logged" in rec for rec in summary['recommendations'])