# Copyright (c) DevSecCode. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root.

"""
Base class for all connectors.

Every connector must subclass ``BaseConnector`` and implement at least
``test_connection()`` and one ``@action``-decorated method.
"""

from __future__ import annotations

import logging
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from typing import Any, Literal

logger = logging.getLogger(__name__)

AuthType = Literal['api_key', 'oauth2', 'basic', 'bearer', 'none']


@dataclass
class ConnectorParam:
	"""Describes a parameter for a connector action."""
	name: str
	type: str = 'string'  # string, number, boolean, json, select
	label: str = ''
	required: bool = False
	default: Any = None
	description: str = ''
	options: list[str] = field(default_factory=list)  # for 'select' type


@dataclass
class ConnectorAction:
	"""Describes an executable action on a connector."""
	id: str
	name: str
	description: str = ''
	params: list[ConnectorParam] = field(default_factory=list)
	method_name: str = ''  # internal: name of the method on the class


@dataclass
class ConnectorDescriptor:
	"""Full metadata describing a connector (for UI presentation)."""
	id: str
	name: str
	description: str = ''
	icon: str = ''  # codicon name
	auth_type: AuthType = 'api_key'
	auth_fields: list[ConnectorParam] = field(default_factory=list)
	actions: list[ConnectorAction] = field(default_factory=list)

	def to_dict(self) -> dict[str, Any]:
		return {
			'id': self.id,
			'name': self.name,
			'description': self.description,
			'icon': self.icon,
			'authType': self.auth_type,
			'authFields': [
				{'name': f.name, 'type': f.type, 'label': f.label or f.name,
				 'required': f.required, 'description': f.description}
				for f in self.auth_fields
			],
			'actions': [
				{'id': a.id, 'name': a.name, 'description': a.description,
				 'params': [
					 {'name': p.name, 'type': p.type, 'label': p.label or p.name,
					  'required': p.required, 'default': p.default,
					  'description': p.description, 'options': p.options}
					 for p in a.params
				 ]}
				for a in self.actions
			],
		}


class BaseConnector(ABC):
	"""Abstract base class for all connectors."""

	# Subclasses should set these (or use @connector decorator)
	connector_id: str = ''
	connector_name: str = ''
	connector_description: str = ''
	connector_icon: str = 'plug'
	auth_type: AuthType = 'api_key'

	def __init__(self, credentials: dict[str, str] | None = None) -> None:
		self._credentials = credentials or {}

	@abstractmethod
	async def test_connection(self) -> bool:
		"""Test that the connector can reach the external service.

		Returns True if connection succeeds, False otherwise.
		"""
		...

	async def execute(self, action_id: str, params: dict[str, Any]) -> Any:
		"""Execute a named action with the given parameters.

		Dispatches to the appropriate @action-decorated method.
		"""
		# Find the method for this action
		for attr_name in dir(self):
			attr = getattr(self, attr_name, None)
			if callable(attr) and getattr(attr, '_action_id', None) == action_id:
				return await attr(**params)
		raise ValueError(f'Unknown action: {action_id} on connector {self.connector_id}')

	def get_descriptor(self) -> ConnectorDescriptor:
		"""Build the connector descriptor from class metadata."""
		actions: list[ConnectorAction] = []
		for attr_name in dir(self):
			attr = getattr(self, attr_name, None)
			if callable(attr) and hasattr(attr, '_action_id'):
				actions.append(ConnectorAction(
					id=attr._action_id,
					name=attr._action_name,
					description=getattr(attr, '_action_description', ''),
					params=getattr(attr, '_action_params', []),
					method_name=attr_name,
				))

		return ConnectorDescriptor(
			id=self.connector_id,
			name=self.connector_name,
			description=self.connector_description,
			icon=self.connector_icon,
			auth_type=self.auth_type,
			auth_fields=getattr(self, '_auth_fields', []),
			actions=actions,
		)
