import json
from abc import ABC, abstractmethod
from urllib import parse as urllib
from lib.common import rest_client
from lib.services.cloud_blueprint.base_client import BaseClient


class RunTasksClient(BaseClient):
    """Service client for Cloud Blueprint Run Tasks API (logs, resources, events, outputs)"""

    def __init__(self, auth_provider, client, **kwargs):
        super().__init__(auth_provider=auth_provider, **kwargs)
        self.client = client

    def _parse_response(self, resp, body, schema):
        """Parse and validate response."""
        if not body or body.strip() == '':
            body_data = {}
        else:
            body_data = json.loads(body)
        self.validate_response(schema, resp, body_data)
        return rest_client.ResponseBody(resp, body_data)

    def list_task_logs(self, deployment_name, run_id, task_id, **params):
        """Tail run task logs."""
        url = self.client.get_task_logs_path(
            self.project_name, deployment_name, run_id, task_id
        )
        if params:
            url += f"?{urllib.urlencode(params)}"
        resp, body = self.get(url)
        return self._parse_response(
            resp, body, self.client.get_schema().list_task_logs
        )

    def show_task_logfile(self, deployment_name, run_id, task_id, **params):
        """Get full log tail from object storage (returns text/plain)."""
        url = self.client.get_task_logfile_path(
            self.project_name, deployment_name, run_id, task_id
        )
        if params:
            url += f"?{urllib.urlencode(params)}"
        resp, body = self.get(url)
        # logfile is text/plain, return raw body
        return rest_client.ResponseBody(resp, body)

    def list_task_resources(self, deployment_name, run_id, task_id):
        """List task resources."""
        url = self.client.get_task_resources_path(
            self.project_name, deployment_name, run_id, task_id
        )
        resp, body = self.get(url)
        return self._parse_response(
            resp, body, self.client.get_schema().list_task_resources
        )

    def list_task_events(self, deployment_name, run_id, task_id, **params):
        """Tail task events (structured)."""
        url = self.client.get_task_events_path(
            self.project_name, deployment_name, run_id, task_id
        )
        if params:
            url += f"?{urllib.urlencode(params)}"
        resp, body = self.get(url)
        return self._parse_response(
            resp, body, self.client.get_schema().list_task_events
        )

    def show_task_eventfile(self, deployment_name, run_id, task_id, **params):
        """Get NDJSON event file tail from object storage."""
        url = self.client.get_task_eventfile_path(
            self.project_name, deployment_name, run_id, task_id
        )
        if params:
            url += f"?{urllib.urlencode(params)}"
        resp, body = self.get(url)
        # eventfile is NDJSON, return raw body
        return rest_client.ResponseBody(resp, body)

    def list_task_outputs(self, deployment_name, run_id, task_id, **params):
        """List task outputs (finalized)."""
        url = self.client.get_task_outputs_path(
            self.project_name, deployment_name, run_id, task_id
        )
        if params:
            url += f"?{urllib.urlencode(params)}"
        resp, body = self.get(url)
        return self._parse_response(
            resp, body, self.client.get_schema().list_task_outputs
        )


class RunTasksClientInterface(ABC):
    """Interface for cloud-specific implementations."""

    @abstractmethod
    def get_schema(self):
        """Return response schemas module."""
        pass

    @abstractmethod
    def get_task_logs_path(self, project_name, deployment_name, run_id, task_id):
        """Return API endpoint path for task logs."""
        pass

    @abstractmethod
    def get_task_logfile_path(self, project_name, deployment_name, run_id, task_id):
        """Return API endpoint path for task logfile."""
        pass

    @abstractmethod
    def get_task_resources_path(self, project_name, deployment_name, run_id, task_id):
        """Return API endpoint path for task resources."""
        pass

    @abstractmethod
    def get_task_events_path(self, project_name, deployment_name, run_id, task_id):
        """Return API endpoint path for task events."""
        pass

    @abstractmethod
    def get_task_eventfile_path(self, project_name, deployment_name, run_id, task_id):
        """Return API endpoint path for task eventfile."""
        pass

    @abstractmethod
    def get_task_outputs_path(self, project_name, deployment_name, run_id, task_id):
        """Return API endpoint path for task outputs."""
        pass
