# Copyright (C) 2012-2024 RhodeCode GmbH
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License, version 3
# (only), as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
# This program is dual-licensed. If you wish to learn more about the
# RhodeCode Enterprise Edition, including its added features, Support services,
# and proprietary license terms, please see https://rhodecode.com/licenses/

import logging

from rhodecode import events
from rhodecode.integrations.types.base import CommitParsingDataHandler, UrlTmpl


log = logging.getLogger(__name__)


class WebhookDataHandler(CommitParsingDataHandler):
    name = "webhook"

    def __init__(self, template_url, headers):
        self.template_url = template_url
        self.headers = headers

    def get_base_parsed_template(self, data):
        """
        initially parses the passed in template with some common variables
        available on ALL calls
        """
        # note: make sure to update the `WEBHOOK_URL_VARS` if this changes
        common_vars = {
            "repo_name": data["repo"]["repo_name"],
            "repo_type": data["repo"]["repo_type"],
            "repo_id": data["repo"]["repo_id"],
            "repo_url": data["repo"]["url"],
            "username": data["actor"]["username"],
            "user_id": data["actor"]["user_id"],
            "event_name": data["name"],
        }

        extra_vars = {}
        for extra_key, extra_val in data["repo"]["extra_fields"].items():
            extra_vars[f"extra__{extra_key}"] = extra_val
        common_vars.update(extra_vars)

        template_url = self.template_url.replace("${extra:", "${extra__")
        for k, v in common_vars.items():
            template_url = UrlTmpl(template_url).safe_substitute(**{k: v})
        return template_url

    def repo_push_event_handler(self, event, data):
        url = self.get_base_parsed_template(data)
        url_calls = []

        branches_commits = self.aggregate_branch_data(data["push"]["branches"], data["push"]["commits"])
        if "${branch}" in url or "${branch_head}" in url or "${commit_id}" in url:
            # call it multiple times, for each branch if used in variables
            for branch, commit_ids in branches_commits.items():
                branch_url = UrlTmpl(url).safe_substitute(branch=branch)

                if "${branch_head}" in branch_url:
                    # last commit in the aggregate is the head of the branch
                    branch_head = commit_ids["branch_head"]
                    branch_url = UrlTmpl(branch_url).safe_substitute(branch_head=branch_head)

                # call further down for each commit if used
                if "${commit_id}" in branch_url:
                    for commit_data in commit_ids["commits"]:
                        commit_id = commit_data["raw_id"]
                        commit_url = UrlTmpl(branch_url).safe_substitute(commit_id=commit_id)
                        # register per-commit call
                        log.debug("register %s call(%s) to url %s", self.name, event, commit_url)
                        url_calls.append((commit_url, self.headers, data))

                else:
                    # register per-branch call
                    log.debug("register %s call(%s) to url %s", self.name, event, branch_url)
                    url_calls.append((branch_url, self.headers, data))

        else:
            log.debug("register %s call(%s) to url %s", self.name, event, url)
            url_calls.append((url, self.headers, data))

        return url_calls

    def repo_commit_comment_handler(self, event, data):
        url = self.get_base_parsed_template(data)
        log.debug("register %s call(%s) to url %s", self.name, event, url)
        comment_vars = [
            ("commit_comment_id", data["comment"]["comment_id"]),
            ("commit_comment_text", data["comment"]["comment_text"]),
            ("commit_comment_type", data["comment"]["comment_type"]),
            ("commit_comment_f_path", data["comment"]["comment_f_path"]),
            ("commit_comment_line_no", data["comment"]["comment_line_no"]),
            ("commit_comment_commit_id", data["commit"]["commit_id"]),
            ("commit_comment_commit_branch", data["commit"]["commit_branch"]),
            ("commit_comment_commit_message", data["commit"]["commit_message"]),
        ]
        for k, v in comment_vars:
            url = UrlTmpl(url).safe_substitute(**{k: v})

        return [(url, self.headers, data)]

    def repo_commit_comment_edit_handler(self, event, data):
        url = self.get_base_parsed_template(data)
        log.debug("register %s call(%s) to url %s", self.name, event, url)
        comment_vars = [
            ("commit_comment_id", data["comment"]["comment_id"]),
            ("commit_comment_text", data["comment"]["comment_text"]),
            ("commit_comment_type", data["comment"]["comment_type"]),
            ("commit_comment_f_path", data["comment"]["comment_f_path"]),
            ("commit_comment_line_no", data["comment"]["comment_line_no"]),
            ("commit_comment_commit_id", data["commit"]["commit_id"]),
            ("commit_comment_commit_branch", data["commit"]["commit_branch"]),
            ("commit_comment_commit_message", data["commit"]["commit_message"]),
        ]
        for k, v in comment_vars:
            url = UrlTmpl(url).safe_substitute(**{k: v})

        return [(url, self.headers, data)]

    def repo_create_event_handler(self, event, data):
        url = self.get_base_parsed_template(data)
        log.debug("register %s call(%s) to url %s", self.name, event, url)
        return [(url, self.headers, data)]

    def pull_request_event_handler(self, event, data):
        url = self.get_base_parsed_template(data)
        log.debug("register %s call(%s) to url %s", self.name, event, url)
        pr_vars = [
            ("pull_request_id", data["pullrequest"]["pull_request_id"]),
            ("pull_request_title", data["pullrequest"]["title"]),
            ("pull_request_url", data["pullrequest"]["url"]),
            ("pull_request_shadow_url", data["pullrequest"]["shadow_url"]),
            ("pull_request_commits_uid", data["pullrequest"]["commits_uid"]),
        ]
        for k, v in pr_vars:
            url = UrlTmpl(url).safe_substitute(**{k: v})

        return [(url, self.headers, data)]

    def __call__(self, event, data):
        if isinstance(event, events.RepoPushEvent):
            return self.repo_push_event_handler(event, data)
        elif isinstance(event, events.RepoCreateEvent):
            return self.repo_create_event_handler(event, data)
        elif isinstance(event, events.RepoCommitCommentEvent):
            return self.repo_commit_comment_handler(event, data)
        elif isinstance(event, events.RepoCommitCommentEditEvent):
            return self.repo_commit_comment_edit_handler(event, data)
        elif isinstance(event, events.PullRequestEvent):
            return self.pull_request_event_handler(event, data)
        else:
            raise ValueError(f"event type `{event.__class__}` has no handler defined")
