from typing import Literal

from libcst._position import CodePosition, CodeRange
from libcst.metadata import CodeSpan
from pydantic import BaseModel

from py_agent.models.base import CustomBaseModel


class Location(BaseModel):
    #: Line numbers are 1-indexed.
    line: int
    #: Column numbers are 0-indexed.
    column: int
    # Indexes are 0-based.
    index: int

    @staticmethod
    def from_position(code_position: CodePosition, index: int) -> "Location":
        return Location(line=code_position.line, column=code_position.column, index=index)


class Position(BaseModel):
    start: Location
    end: Location

    @staticmethod
    def from_metadata(code_range: CodeRange, code_span: CodeSpan) -> "Position":
        start_index = code_span.start
        end_index = start_index + code_span.length
        start = Location.from_position(code_range.start, start_index)
        end = Location.from_position(code_range.end, end_index)
        return Position(start=start, end=end)


TestableType = Literal["function", "method", "class"]


class Testable(CustomBaseModel):
    type: TestableType
    name: str
    position: Position
    parent_name: str | None = None
    can_create_tests: bool = True
    file_path: str = ""

    @staticmethod
    def get_type(testable_type: TestableType, class_name: str | None) -> TestableType:
        if class_name is None:
            return testable_type
        if testable_type == "class":
            return "class"
        return "method"


class TestablesDTO(CustomBaseModel):
    file_path: str
    testables: list[Testable]
