"""
Issue Activity Tracking Service

This module provides comprehensive issue activity tracking functionality,
building on the existing database infrastructure to log and retrieve
issue activities for cost allocation and project management.
"""

import sqlite3
from datetime import datetime, date
from typing import List, Dict, Any, Optional
from dataclasses import dataclass, asdict
from enum import Enum

from markitect.finance.models import FinanceModels


class ActivityType(Enum):
    """Enumeration of supported issue activity types."""
    CREATED = "created"
    MODIFIED = "modified"
    CLOSED = "closed"
    REOPENED = "reopened"
    COMMENTED = "commented"
    STATUS_CHANGED = "status_changed"


@dataclass
class IssueActivity:
    """Data class representing an issue activity record with convenient methods."""
    id: Optional[int] = None
    issue_id: int = None
    activity_type: ActivityType = None
    activity_date: date = None
    period_id: Optional[int] = None
    activity_details: Optional[str] = None
    created_at: Optional[datetime] = None

    @property
    def activity_type_value(self) -> str:
        """Get the string value of the activity type."""
        return self.activity_type.value if self.activity_type else ''

    @property
    def activity_type_display(self) -> str:
        """Get the display-friendly activity type."""
        return self.activity_type_value.replace('_', ' ').title()

    @property
    def formatted_date(self) -> str:
        """Get formatted activity date string."""
        return self.activity_date.strftime('%Y-%m-%d') if self.activity_date else 'N/A'

    @property
    def formatted_datetime(self) -> str:
        """Get formatted created datetime string."""
        return self.created_at.strftime('%Y-%m-%d %H:%M') if self.created_at else 'N/A'

    @property
    def truncated_details(self) -> str:
        """Get truncated activity details for display (max 40 chars)."""
        if not self.activity_details:
            return ''
        return (self.activity_details[:40] + '...') if len(self.activity_details) > 40 else self.activity_details

    def contains_keyword(self, keyword: str, case_sensitive: bool = False) -> bool:
        """Check if activity contains a keyword in type or details."""
        search_text = f"{self.activity_type_value} {self.activity_details or ''}".strip()
        if not case_sensitive:
            search_text = search_text.lower()
            keyword = keyword.lower()
        return keyword in search_text

    def has_implementation_activity(self) -> bool:
        """Check if this activity indicates implementation work."""
        return (self.contains_keyword('implement') or
                self.contains_keyword('code') or
                self.contains_keyword('develop'))

    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary representation."""
        return {
            'id': self.id,
            'issue_id': self.issue_id,
            'activity_type': self.activity_type_value,
            'activity_date': self.activity_date.isoformat() if self.activity_date else None,
            'period_id': self.period_id,
            'activity_details': self.activity_details,
            'created_at': self.created_at.isoformat() if self.created_at else None
        }


class IssueActivityTracker:
    """
    Service for tracking and managing issue activities.

    Provides functionality to log issue activities, retrieve activity history,
    and generate activity reports for cost allocation and project management.
    """

    def __init__(self, db_path: str = "markitect.db"):
        """
        Initialize the issue activity tracker.

        Args:
            db_path: Path to the SQLite database file
        """
        self.db_path = db_path
        self.finance_models = FinanceModels(db_path)
        self._ensure_schema()

    def _ensure_schema(self):
        """Ensure the database schema is properly initialized."""
        self.finance_models.initialize_finance_schema()

    def log_activity(
        self,
        issue_id: int,
        activity_type: ActivityType,
        activity_date: Optional[date] = None,
        activity_details: Optional[str] = None,
        period_id: Optional[int] = None
    ) -> int:
        """
        Log an issue activity.

        Args:
            issue_id: ID of the issue
            activity_type: Type of activity performed
            activity_date: Date when activity occurred (defaults to today)
            activity_details: Additional details about the activity
            period_id: Optional period ID for cost allocation

        Returns:
            ID of the created activity record

        Raises:
            sqlite3.Error: If database operation fails
        """
        if activity_date is None:
            activity_date = date.today()

        with self.finance_models.get_connection() as conn:
            cursor = conn.cursor()

            # If period_id is not provided, try to get the current period
            if period_id is None:
                cursor.execute('''
                    SELECT id FROM cost_periods
                    WHERE ? BETWEEN period_start AND period_end
                    ORDER BY created_at DESC LIMIT 1
                ''', (activity_date.isoformat(),))
                result = cursor.fetchone()
                if result:
                    period_id = result[0]

            cursor.execute('''
                INSERT INTO issue_activity_log
                (issue_id, activity_type, activity_date, period_id, activity_details)
                VALUES (?, ?, ?, ?, ?)
            ''', (issue_id, activity_type.value, activity_date.isoformat(), period_id, activity_details))

            return cursor.lastrowid

    def get_issue_activities(
        self,
        issue_id: int,
        limit: Optional[int] = None,
        offset: int = 0
    ) -> List[IssueActivity]:
        """
        Get activities for a specific issue.

        Args:
            issue_id: ID of the issue
            limit: Maximum number of activities to return
            offset: Number of activities to skip

        Returns:
            List of issue activities, ordered by activity date (most recent first)
        """
        with self.finance_models.get_connection() as conn:
            cursor = conn.cursor()

            query = '''
                SELECT id, issue_id, activity_type, activity_date,
                       period_id, activity_details, created_at
                FROM issue_activity_log
                WHERE issue_id = ?
                ORDER BY activity_date DESC, created_at DESC
            '''
            params = [issue_id]

            if limit:
                query += ' LIMIT ? OFFSET ?'
                params.extend([limit, offset])

            cursor.execute(query, params)
            rows = cursor.fetchall()

            activities = []
            for row in rows:
                activity = IssueActivity(
                    id=row[0],
                    issue_id=row[1],
                    activity_type=ActivityType(row[2]),
                    activity_date=datetime.strptime(row[3], '%Y-%m-%d').date() if row[3] else None,
                    period_id=row[4],
                    activity_details=row[5],
                    created_at=datetime.fromisoformat(row[6]) if row[6] else None
                )
                activities.append(activity)

            return activities

    def get_activities_by_period(
        self,
        period_id: int,
        activity_types: Optional[List[ActivityType]] = None
    ) -> List[IssueActivity]:
        """
        Get all activities within a specific cost period.

        Args:
            period_id: ID of the cost period
            activity_types: Optional list of activity types to filter by

        Returns:
            List of issue activities within the period
        """
        with self.finance_models.get_connection() as conn:
            cursor = conn.cursor()

            query = '''
                SELECT id, issue_id, activity_type, activity_date,
                       period_id, activity_details, created_at
                FROM issue_activity_log
                WHERE period_id = ?
            '''
            params = [period_id]

            if activity_types:
                placeholders = ','.join(['?' for _ in activity_types])
                query += f' AND activity_type IN ({placeholders})'
                params.extend([at.value for at in activity_types])

            query += ' ORDER BY activity_date DESC, created_at DESC'

            cursor.execute(query, params)
            rows = cursor.fetchall()

            activities = []
            for row in rows:
                activity = IssueActivity(
                    id=row[0],
                    issue_id=row[1],
                    activity_type=ActivityType(row[2]),
                    activity_date=datetime.strptime(row[3], '%Y-%m-%d').date() if row[3] else None,
                    period_id=row[4],
                    activity_details=row[5],
                    created_at=datetime.fromisoformat(row[6]) if row[6] else None
                )
                activities.append(activity)

            return activities

    def get_activity_summary(
        self,
        issue_id: Optional[int] = None,
        start_date: Optional[date] = None,
        end_date: Optional[date] = None
    ) -> Dict[str, Any]:
        """
        Get activity summary statistics.

        Args:
            issue_id: Optional issue ID to filter by
            start_date: Optional start date filter
            end_date: Optional end date filter

        Returns:
            Dictionary containing activity summary statistics
        """
        with self.finance_models.get_connection() as conn:
            cursor = conn.cursor()

            # Build base query
            base_conditions = []
            params = []

            if issue_id:
                base_conditions.append('issue_id = ?')
                params.append(issue_id)

            if start_date:
                base_conditions.append('activity_date >= ?')
                params.append(start_date.isoformat() if hasattr(start_date, 'isoformat') else start_date)

            if end_date:
                base_conditions.append('activity_date <= ?')
                params.append(end_date.isoformat() if hasattr(end_date, 'isoformat') else end_date)

            where_clause = ' AND '.join(base_conditions) if base_conditions else '1=1'

            # Get total activity count
            cursor.execute(f'''
                SELECT COUNT(*) FROM issue_activity_log WHERE {where_clause}
            ''', params)
            total_activities = cursor.fetchone()[0]

            # Get activity count by type
            cursor.execute(f'''
                SELECT activity_type, COUNT(*)
                FROM issue_activity_log
                WHERE {where_clause}
                GROUP BY activity_type
                ORDER BY COUNT(*) DESC
            ''', params)
            activities_by_type = dict(cursor.fetchall())

            # Get unique issues count
            cursor.execute(f'''
                SELECT COUNT(DISTINCT issue_id)
                FROM issue_activity_log
                WHERE {where_clause}
            ''', params)
            unique_issues = cursor.fetchone()[0]

            # Get date range
            cursor.execute(f'''
                SELECT MIN(activity_date), MAX(activity_date)
                FROM issue_activity_log
                WHERE {where_clause}
            ''', params)
            date_range = cursor.fetchone()

            return {
                'total_activities': total_activities,
                'activities_by_type': activities_by_type,
                'unique_issues': unique_issues,
                'date_range': {
                    'start': date_range[0] if date_range[0] else None,
                    'end': date_range[1] if date_range[1] else None
                },
                'filters': {
                    'issue_id': issue_id,
                    'start_date': start_date.isoformat() if start_date else None,
                    'end_date': end_date.isoformat() if end_date else None
                }
            }

    def delete_activity(self, activity_id: int) -> bool:
        """
        Delete an activity record.

        Args:
            activity_id: ID of the activity to delete

        Returns:
            True if activity was deleted, False if not found
        """
        with self.finance_models.get_connection() as conn:
            cursor = conn.cursor()
            cursor.execute('DELETE FROM issue_activity_log WHERE id = ?', (activity_id,))
            return cursor.rowcount > 0

    def bulk_log_activities(self, activities: List[Dict[str, Any]]) -> List[int]:
        """
        Log multiple activities in a single transaction.

        Args:
            activities: List of activity dictionaries with keys:
                       issue_id, activity_type, activity_date (optional),
                       activity_details (optional), period_id (optional)

        Returns:
            List of created activity IDs

        Raises:
            ValueError: If activity data is invalid
            sqlite3.Error: If database operation fails
        """
        activity_ids = []

        with self.finance_models.get_connection() as conn:
            cursor = conn.cursor()

            for activity_data in activities:
                if 'issue_id' not in activity_data or 'activity_type' not in activity_data:
                    raise ValueError("Each activity must have 'issue_id' and 'activity_type'")

                issue_id = activity_data['issue_id']
                activity_type = ActivityType(activity_data['activity_type'])
                activity_date = activity_data.get('activity_date', date.today())
                activity_details = activity_data.get('activity_details')
                period_id = activity_data.get('period_id')

                # Auto-determine period if not provided
                if period_id is None:
                    cursor.execute('''
                        SELECT id FROM cost_periods
                        WHERE ? BETWEEN period_start AND period_end
                        ORDER BY created_at DESC LIMIT 1
                    ''', (activity_date.isoformat() if hasattr(activity_date, 'isoformat') else activity_date,))
                    result = cursor.fetchone()
                    if result:
                        period_id = result[0]

                cursor.execute('''
                    INSERT INTO issue_activity_log
                    (issue_id, activity_type, activity_date, period_id, activity_details)
                    VALUES (?, ?, ?, ?, ?)
                ''', (issue_id, activity_type.value, activity_date.isoformat() if hasattr(activity_date, 'isoformat') else activity_date, period_id, activity_details))

                activity_ids.append(cursor.lastrowid)

        return activity_ids