"""
High-level API client that converts between API responses and domain models.
"""

from datetime import datetime
from typing import List, Optional, Dict, Any

from .http_client import GiteaHttpClient
from .models import Issue, Milestone, Label, User, IssueCreateData, IssueUpdateData, MilestoneCreateData, LabelCreateData
from .config import GiteaConfig
from .exceptions import GiteaNotFoundError, GiteaError


class GiteaApiClient:
    """High-level API client with domain model conversion."""

    def __init__(self, config: GiteaConfig):
        self.config = config
        self.http = GiteaHttpClient(config)

    # Issue operations
    def get_issue(self, issue_number: int) -> Issue:
        """Get a specific issue by number."""
        try:
            url = f"{self.config.issues_api_url}/{issue_number}"
            data = self.http.get(url)
            return self._parse_issue(data)
        except GiteaError as e:
            if "not found" in str(e).lower():
                raise GiteaNotFoundError(f"Issue #{issue_number} not found")
            raise

    def list_issues(self, state: str = "all", page: int = 1, per_page: int = 50) -> List[Issue]:
        """List issues with optional filtering."""
        params = {"page": str(page), "limit": str(per_page)}
        if state != "all":
            params["state"] = state

        data = self.http.get(self.config.issues_api_url, params)

        if not isinstance(data, list):
            raise GiteaError("Invalid response format: expected list of issues")

        return [self._parse_issue(issue_data) for issue_data in data]

    def create_issue(self, issue_data: IssueCreateData) -> Issue:
        """Create a new issue."""
        payload = {
            "title": issue_data.title,
            "body": issue_data.body,
        }

        if issue_data.assignees:
            payload["assignees"] = issue_data.assignees
        if issue_data.milestone:
            payload["milestone"] = issue_data.milestone
        if issue_data.labels:
            # Convert label names to label IDs
            payload["labels"] = self._resolve_label_ids(issue_data.labels)

        data = self.http.post(self.config.issues_api_url, payload)
        return self._parse_issue(data)

    def update_issue(self, issue_number: int, update_data: IssueUpdateData) -> Issue:
        """Update an existing issue."""
        payload = {}

        if update_data.title is not None:
            payload["title"] = update_data.title
        if update_data.body is not None:
            payload["body"] = update_data.body
        if update_data.state is not None:
            payload["state"] = update_data.state
        if update_data.assignees is not None:
            payload["assignees"] = update_data.assignees
        if update_data.milestone is not None:
            payload["milestone"] = update_data.milestone
        if update_data.labels is not None:
            payload["labels"] = update_data.labels

        url = f"{self.config.issues_api_url}/{issue_number}"
        data = self.http.patch(url, payload)
        return self._parse_issue(data)

    # Milestone operations
    def list_milestones(self, state: str = "all") -> List[Milestone]:
        """List repository milestones."""
        params = {}
        if state != "all":
            params["state"] = state

        data = self.http.get(self.config.milestones_api_url, params)

        if not isinstance(data, list):
            raise GiteaError("Invalid response format: expected list of milestones")

        return [self._parse_milestone(milestone_data) for milestone_data in data]

    def create_milestone(self, milestone_data: MilestoneCreateData) -> Milestone:
        """Create a new milestone."""
        payload = {
            "title": milestone_data.title,
            "description": milestone_data.description,
        }

        if milestone_data.due_on:
            payload["due_on"] = milestone_data.due_on

        data = self.http.post(self.config.milestones_api_url, payload)
        return self._parse_milestone(data)

    # Label operations
    def list_labels(self) -> List[Label]:
        """List repository labels."""
        data = self.http.get(self.config.labels_api_url)

        if not isinstance(data, list):
            raise GiteaError("Invalid response format: expected list of labels")

        return [self._parse_label(label_data) for label_data in data]

    def create_label(self, label_data: LabelCreateData) -> Label:
        """Create a new label."""
        payload = {
            "name": label_data.name,
            "color": label_data.color,
            "description": label_data.description,
        }

        data = self.http.post(self.config.labels_api_url, payload)
        return self._parse_label(data)

    # Parsing methods
    def _parse_issue(self, data: Dict[str, Any]) -> Issue:
        """Parse issue data from API response."""
        try:
            # Parse labels
            labels = []
            if data.get('labels'):
                labels = [self._parse_label(label_data) for label_data in data['labels']]

            # Parse assignee
            assignee = None
            if data.get('assignee'):
                assignee = self._parse_user(data['assignee'])

            # Parse milestone
            milestone = None
            if data.get('milestone'):
                milestone = self._parse_milestone(data['milestone'])

            # Check if this is an error response
            if 'message' in data and 'url' in data and 'number' not in data and 'id' not in data:
                raise GiteaError(f"API Error: {data.get('message', 'Unknown error')} (URL: {data.get('url', 'N/A')})")

            # Handle both 'number' and 'id' fields (Gitea API might use either)
            issue_number = data.get('number') or data.get('id')
            if issue_number is None:
                raise GiteaError(f"Issue response missing both 'number' and 'id' fields. Available fields: {list(data.keys())}")

            return Issue(
                number=issue_number,
                title=data['title'],
                body=data.get('body', ''),
                state=data['state'],
                created_at=self._parse_datetime(data['created_at']),
                updated_at=self._parse_datetime(data['updated_at']),
                html_url=data['html_url'],
                assignee=assignee,
                labels=labels,
                milestone=milestone
            )
        except (KeyError, ValueError) as e:
            raise GiteaError(f"Failed to parse issue data: {e}")

    def _parse_milestone(self, data: Dict[str, Any]) -> Milestone:
        """Parse milestone data from API response."""
        return Milestone(
            id=data['id'],
            title=data['title'],
            description=data.get('description', ''),
            state=data['state'],
            open_issues=data.get('open_issues', 0),
            closed_issues=data.get('closed_issues', 0),
            due_on=data.get('due_on'),
            created_at=self._parse_datetime(data.get('created_at')) if data.get('created_at') else None,
            updated_at=self._parse_datetime(data.get('updated_at')) if data.get('updated_at') else None
        )

    def _parse_label(self, data: Dict[str, Any]) -> Label:
        """Parse label data from API response."""
        return Label(
            id=data['id'],
            name=data['name'],
            color=data['color'],
            description=data.get('description', '')
        )

    def _parse_user(self, data: Dict[str, Any]) -> User:
        """Parse user data from API response."""
        return User(
            id=data['id'],
            login=data['login'],
            full_name=data.get('full_name', ''),
            email=data.get('email', ''),
            avatar_url=data.get('avatar_url', '')
        )

    def _parse_datetime(self, date_str: str) -> datetime:
        """Parse datetime from API response."""
        # Remove Z and microseconds for consistent parsing
        date_str = date_str.replace('Z', '').split('.')[0]
        return datetime.strptime(date_str, '%Y-%m-%dT%H:%M:%S')

    def _resolve_label_ids(self, label_names: List[str]) -> List[int]:
        """Convert label names to label IDs for API calls."""
        try:
            # Get all labels for the repository
            labels_data = self.http.get(self.config.labels_api_url)
            if not isinstance(labels_data, list):
                raise GiteaError("Invalid labels response format")

            # Create name-to-ID mapping
            label_map = {label_data['name']: label_data['id'] for label_data in labels_data}

            # Resolve names to IDs
            label_ids = []
            for name in label_names:
                if name in label_map:
                    label_ids.append(label_map[name])
                else:
                    # If label doesn't exist, we could create it or skip it
                    # For now, let's skip non-existent labels
                    print(f"Warning: Label '{name}' not found, skipping")

            return label_ids

        except Exception as e:
            # If label resolution fails, proceed without labels rather than failing entirely
            print(f"Warning: Could not resolve labels: {e}")
            return []