# Copyright (C) 2024 Cetmix OÜ
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

import logging

from odoo import api, fields, models

_logger = logging.getLogger(__name__)


class CxTowerServer(models.Model):
    _inherit = "cx.tower.server"

    git_project_rel_ids = fields.One2many(
        comodel_name="cx.tower.git.project.rel",
        inverse_name="server_id",
        copy=False,
        depends=["git_project_ids"],
        groups="cetmix_tower_server.group_manager,cetmix_tower_server.group_root",
    )

    # Helper field to get all git projects related to server
    # IMPORTANT: This field may contain duplicates because of the relation nature!
    git_project_ids = fields.Many2many(
        comodel_name="cx.tower.git.project",
        relation="cx_tower_git_project_rel",
        column1="server_id",
        column2="git_project_id",
        readonly=True,
        copy=False,
        depends=["git_project_rel_ids"],
        groups="cetmix_tower_server.group_manager,cetmix_tower_server.group_root",
    )

    # ------------------------------
    # YAML mixin methods
    # ------------------------------
    def _get_fields_for_yaml(self):
        res = super()._get_fields_for_yaml()
        res += [
            "git_project_rel_ids",
        ]
        return res

    def _get_force_x2m_resolve_models(self):
        res = super()._get_force_x2m_resolve_models()

        # Add File in order to always try to use existing one
        res += ["cx.tower.file"]
        return res

    def _update_or_create_related_record(
        self, model, reference, values, create_immediately=False
    ):
        # Files must be created immediately because they are related
        # to both server and git project.
        # So if a file is not created immediately when it is created
        # for the server, the same file will be created for the git project.
        # This will lead to creation of two files with the same content
        # for the same server.

        if model._name == "cx.tower.file":
            create_immediately = True
        return super()._update_or_create_related_record(
            model, reference, values, create_immediately=create_immediately
        )

    @api.model
    def get_servers_by_git_ref(self, repository_url, head=None, head_type=None):
        """
        Return servers linked to a given Git repository reference.

        Parameters
        ----------
        repository_url : str
            Pre-normalized canonical Git URL
            (e.g. ``https://host/owner/repo.git``).
        head : str, optional
            Branch name, commit SHA, or PR identifier.
        head_type : {'branch', 'commit', 'pr'}, optional
            Type of the ``head`` argument.
            If only ``head`` is provided, it will match across all head types.
            If only ``head_type`` is provided, it will filter by type regardless of head

        Returns
        -------
        recordset of cx.tower.server
            Matching servers. Empty recordset if no matches.
        """

        server_obj = self.env["cx.tower.server"]
        # URL MUST be already canonical.
        if not repository_url:
            return server_obj

        # Get repository id by URL
        repo_id = self.env["cx.tower.git.repo"]._get_repo_id_by_url(
            repository_url, raise_if_invalid=False
        )
        if not repo_id:
            return server_obj
        repo = self.env["cx.tower.git.repo"].browse(repo_id)

        # Compose domain for remotes
        remote_domain = [
            ("source_id.enabled", "=", True),
            ("enabled", "=", True),
        ]
        if head:
            head = self.env["cx.tower.git.remote"]._sanitize_head(head)
            remote_domain.append(("head", "=", head))
        if head_type:
            remote_domain.append(("head_type", "=", head_type))

        # Get remotes
        remotes = repo.remote_ids.filtered_domain(remote_domain)
        if not remotes:
            return server_obj

        # Get servers from remotes
        servers = remotes.mapped("git_project_id.git_project_rel_ids.server_id")
        return servers

    def _command_runner_file_using_template_create_file(
        self,
        log_record,
        server_dir,
        **kwargs,
    ):
        """Override to create git project relation
        when creating a file using a template.
        """
        file = super()._command_runner_file_using_template_create_file(
            log_record, server_dir, **kwargs
        )
        if file:
            # Get the flight plan line from log record
            plan_line = log_record.plan_log_id.plan_line_executed_id
            # Try to get git project from custom values
            custom_values = log_record.variable_values
            git_project_reference = custom_values and custom_values.get(
                "__git_project__"
            )
            if git_project_reference:
                git_project = self.env["cx.tower.git.project"].get_by_reference(
                    git_project_reference
                )
                if not git_project:
                    _logger.warning(
                        "Git project '%s' provided with the `__git_project__` "
                        "custom value not found for server '%s' "
                        "in flight plan line '%s' "
                        "of the flight plan '%s'. "
                        "No project was linked to the file '%s'.",
                        git_project_reference,
                        self.name,
                        plan_line.name,
                        log_record.plan_log_id.plan_id.name,
                        file.name,
                    )

            # Try to get git project set explicitly in the flight plan line
            else:
                git_project = plan_line.git_project_id
            if not git_project:
                return file

            if plan_line.is_make_copy:
                # Remove default_server_ids from context, because this relation
                # will be created through git_project_rel_ids.
                # default_server_ids will interfere at the moment when
                # pairs of values are created through SQL query
                # in the method write_real and it does not take into account
                # that in this case we are creating a copy of the git project
                git_project = git_project.with_context(default_server_ids=False).copy(
                    {"name": git_project._compose_copy_name(server=self)}
                )

            self.env["cx.tower.git.project.rel"].create(
                {
                    "git_project_id": git_project.id,
                    "server_id": self.id,
                    "file_id": file.id,
                    "project_format": git_project._default_project_format(),
                }
            )
        return file
