import os

from osa_tool.analytics.metadata import RepositoryMetadata
from osa_tool.analytics.sourcerank import SourceRank
from osa_tool.config.settings import ConfigLoader
from osa_tool.models.models import ModelHandler, ModelHandlerFactory
from osa_tool.readmegen.context.article_content import PdfParser
from osa_tool.readmegen.context.article_path import get_pdf_path
from osa_tool.readmegen.context.files_contents import FileProcessor
from osa_tool.readmegen.postprocessor.response_cleaner import JsonProcessor
from osa_tool.readmegen.utils import extract_example_paths
from osa_tool.utils.logger import logger
from osa_tool.utils.prompts_builder import PromptLoader, PromptBuilder
from osa_tool.utils.utils import parse_folder_name, extract_readme_content


class LLMClient:
    def __init__(self, config_loader: ConfigLoader, prompts: PromptLoader, metadata: RepositoryMetadata):
        self.config_loader = config_loader
        self.prompts = prompts
        self.metadata = metadata
        self.config = self.config_loader.config
        self.model_handler: ModelHandler = ModelHandlerFactory.build(self.config)
        self.sourcerank = SourceRank(config_loader)
        self.tree = self.sourcerank.tree

        self.repo_url = self.config.git.repository
        self.base_path = os.path.join(os.getcwd(), parse_folder_name(self.repo_url))
        self.readme_content = extract_readme_content(self.base_path)

    def get_responses(self) -> tuple[str, str, str]:
        """
        Extracts core features, overview, and optionally a Getting Started section from the repository.

        This method processes key files to extract core features and a general overview of the project.
        If example files are detected (e.g., scripts or notebooks demonstrating usage), it also generates
        a Getting Started section. Each part is generated by sending prompts to the model and cleaned before returning.

        Returns:
            tuple[str, str, str]: A tuple containing:
                - core_features: Main functionalities and capabilities of the project.
                - overview: General description and project context.
                - getting_started: (Optional) Setup or usage guide if examples are available.
        """
        logger.info("Started generating README-style summary.")
        key_files = self.get_key_files()
        key_files_content = FileProcessor(self.config_loader, key_files).process_files()

        logger.info("Generating core features of the project...")
        core_raw = self.model_handler.send_request(
            PromptBuilder.render(
                self.prompts.get("readme.core_features"),
                project_name=self.metadata.name,
                metadata=self.metadata,
                readme_content=self.readme_content,
                key_files_content=FileProcessor.serialize_file_contexts(key_files_content),
            )
        )
        core_features = JsonProcessor.parse(core_raw, expected_type=str)

        logger.info("Generating project overview...")
        overview_raw = self.model_handler.send_request(
            PromptBuilder.render(
                self.prompts.get("readme.overview"),
                project_name=self.metadata.name,
                description=self.metadata.description,
                readme_content=self.readme_content,
                core_features=core_features,
            )
        )
        overview = JsonProcessor.parse(overview_raw, expected_key="overview", expected_type=str)

        getting_started = self.get_getting_started()

        logger.info("README-style summary generation completed.")
        return core_features, overview, getting_started

    def get_responses_article(self, article: str) -> tuple[str, str, str, str]:
        """
        Generates an article-style summary of the repository based on key files and associated PDF documentation.

        Args:
            article (str): Path to a research or descriptive PDF document associated with the project.

        Returns:
            tuple[str, str, str]: A tuple containing:
             - overview: General description and project context.
             - content: Content section based on key files and documentation.
             - algorithms: Description of algorithms used.
        """
        logger.info("Started generating Article-style summary.")
        key_files = self.get_key_files()
        key_files_content = FileProcessor(self.config_loader, key_files).process_files()

        logger.info("Generating summary of key files...")
        files_summary = self.model_handler.send_request(
            PromptBuilder.render(
                self.prompts.get("readme_article.file_summary"),
                files_content=FileProcessor.serialize_file_contexts(key_files_content),
                readme_content=self.readme_content,
            )
        )

        path_to_pdf = get_pdf_path(article)
        pdf_content = PdfParser(path_to_pdf).data_extractor()

        logger.info("Generating summary of PDF content...")
        pdf_summary = self.model_handler.send_request(
            PromptBuilder.render(self.prompts.get("readme_article.pdf_summary"), pdf_content=pdf_content)
        )

        logger.info("Generating project overview from combined sources...")
        overview_raw = self.model_handler.send_request(
            PromptBuilder.render(
                self.prompts.get("readme_article.overview"),
                project_name=self.metadata.name,
                files_summary=files_summary,
                pdf_summary=pdf_summary,
                readme_content=self.readme_content,
            )
        )
        overview = JsonProcessor.parse(overview_raw, expected_key="overview", expected_type=str)

        logger.info("Generating content section...")
        content_raw = self.model_handler.send_request(
            PromptBuilder.render(
                self.prompts.get("readme_article.content"),
                project_name=self.metadata.name,
                pdf_summary=pdf_summary,
                files_summary=files_summary,
                readme_content=self.readme_content,
            )
        )
        content = JsonProcessor.parse(content_raw, expected_key="content", expected_type=str)

        logger.info("Generating algorithm description...")
        algorithms_raw = self.model_handler.send_request(
            PromptBuilder.render(
                self.prompts.get("readme_article.algorithms"),
                project_name=self.metadata.name,
                files_content=FileProcessor.serialize_file_contexts(key_files_content),
                pdf_summary=pdf_summary,
                readme_content=self.readme_content,
            )
        )
        algorithms = JsonProcessor.parse(algorithms_raw, expected_key="algorithms", expected_type=str)

        getting_started = self.get_getting_started()

        logger.info("Article-style summary generation completed.")
        return overview, content, algorithms, getting_started

    def get_key_files(self) -> list:
        """Identifies key files from the project repository using model analysis."""
        response = self.model_handler.send_request(
            PromptBuilder.render(
                self.prompts.get("readme.preanalysis"),
                repository_tree=self.tree,
                readme_content=self.readme_content,
            )
        )
        data = JsonProcessor.parse(response, expected_key="key_files", expected_type=list)
        return data or []

    def get_getting_started(self) -> str:
        """
        Attempting to generate Getting Started section using
        information from README, examples and docs directories
        """
        logger.info("Attempting to generate Getting Started section...")
        examples_files = extract_example_paths(self.tree)
        examples_content = FileProcessor(self.config_loader, examples_files).process_files()
        getting_started_raw = self.model_handler.send_request(
            PromptBuilder.render(
                self.prompts.get("readme.getting_started"),
                project_name=self.metadata.name,
                readme_content=self.readme_content,
                examples_files_content=FileProcessor.serialize_file_contexts(examples_content),
            )
        )
        return JsonProcessor.parse(getting_started_raw, expected_key="getting_started", expected_type=str)

    def get_citation_from_readme(self) -> str:
        logger.info("Detecting citations in README...")
        citation_raw = self.model_handler.send_request(
            PromptBuilder.render(
                self.prompts.get("readme.citation"),
                readme=self.readme_content,
            )
        )
        citation = JsonProcessor.parse(citation_raw, expected_key="citation", expected_type=str)
        return citation

    def refine_readme(self, generated_readme: str) -> str:
        logger.info("Refining README files...")
        refine_step1 = self.model_handler.send_request(
            PromptBuilder.render(
                self.prompts.get("readme.refine_step1"),
                old_readme=self.readme_content,
                new_readme=generated_readme,
            )
        )
        refine_step1 = JsonProcessor.parse(refine_step1, expected_key="readme", expected_type=str)

        refine_step2 = self.model_handler.send_request(
            PromptBuilder.render(self.prompts.get("readme.refine_step2"), readme=refine_step1)
        )
        refine_step2 = JsonProcessor.parse(refine_step2, expected_key="readme", expected_type=str)

        refine_step3 = self.model_handler.send_request(
            PromptBuilder.render(self.prompts.get("readme.refine_step3"), readme=refine_step2)
        )
        refine_step3 = JsonProcessor.parse(refine_step3, expected_key="readme", expected_type=str)
        return refine_step3

    def clean(self, readme: str) -> str:
        logger.info("Cleaning README...")
        clean_step1 = self.model_handler.send_request(
            PromptBuilder.render(self.prompts.get("readme.clean_step1"), readme=readme)
        )
        clean_step1 = JsonProcessor.parse(clean_step1, expected_key="readme", expected_type=str)

        clean_step2 = self.model_handler.send_request(
            PromptBuilder.render(self.prompts.get("readme.clean_step2"), readme=clean_step1)
        )
        clean_step2 = JsonProcessor.parse(clean_step2, expected_key="readme", expected_type=str)

        clean_step3 = self.model_handler.send_request(
            PromptBuilder.render(self.prompts.get("readme.clean_step3"), readme=clean_step2)
        )
        clean_step3 = JsonProcessor.parse(clean_step3, expected_key="readme", expected_type=str)
        return clean_step3

    def get_article_name(self, pdf_content: str) -> str:
        logger.info("Getting article name from pdf...")
        article_name = self.model_handler.send_request(
            PromptBuilder.render(self.prompts.get("readme_article.article_name_extraction"), pdf_content=pdf_content)
        )
        article_name = JsonProcessor.parse(article_name, expected_key="article_name", expected_type=str)
        return article_name
