import re
import os
from datetime import datetime
from actions_logging.app_logging import logger
from github.env import get_required_env_var, exit_on_error_and_write_summary, write_github_env
from github.github_apis import get_tags, get_commits_after_last_tag
from github.constants import GH_URL, GITHUB_REPOSITORY
from git.constants import ENV_FILE_VERSION, PR_NUMBER, GITHUB_RUN_NUMBER, RELEASE_TAGS_FILE, MS_TAG_VERSION
from jira.consts import JIRA_TICKET, JIRA_HOST
from jira.jira_ticket import get_jira_ticket_info
from jira.models import JiraTicket

RELEASE_TMP_FILE_NAME = f'tmp_release_notes_{int(datetime.now().timestamp())}.txt'
CHANGELOG_FILE_NAME = 'CHANGELOG.md'
RELEASE_NOTES_VERY_BEGINNING_TEXT = 'Very beginning of Release notes.'
RELEASE_DATE_FORMAT = '%a %b %d %H:%M:%S %Z %Y'
CHANGELOG_DATE_FORMAT = '%Y-%m-%d'
CHANGELOG_NO_RELEASE_NOTES_TEXT = 'No release notes.'
CHANGELOG_VERY_BEGINNING_TEXT = 'Very beginning of Changelog.'
CHANGELOG_TITLE = '# Changelog'
CHANGELOG_VERSION_PATTERN_REGEXP = r"##\s?\[\s?v.*"
CHANGELOG_VERSION_DETECTION_ROWS_COUNT = 40
CHANGELOG_DESCRIPTION_ITEM_HEADER = '### Added'
CHANGELOG_HEADER_TEMPLATE = '## %PREFIX%%TAGS%%SUFFIX%%MID_SEPARATOR%%DATE%'
CHANGELOG_HEADER_PREFIX = '['
CHANGELOG_HEADER_SUFFIX = ']'
CHANGELOG_TAG_SEPARATOR = ', '
CHANGELOG_HEADER_MID_SEPARATOR = ' - '
CHANGELOG_ITEM_PREFIX = '- '
CHANGELOG_ITEM_SUFFIX = '<br />'


def check_file_exists(file_path):
    if not os.path.exists(file_path):
        return False
    return True


def insert_content_before_match_or_from_top(file_path: str, line_to_insert: str):
    try:
        version_regex = re.compile(CHANGELOG_VERSION_PATTERN_REGEXP)
        beginning_message_regex = re.compile(rf'{CHANGELOG_VERY_BEGINNING_TEXT}')

        with open(file_path, 'r') as file:
            lines = file.readlines()

        matched_version_line = None
        insert_from_top = False
        should_update_file = False

        for i, line in enumerate(lines):
            # if version not found in first CHANGELOG_VERSION_DETECTION_ROWS_COUNT rows
            # then CHANGELOG.md seems broken, fixing it by inserting from top CHANGELOG_TITLE and line_to_insert
            if i == CHANGELOG_VERSION_DETECTION_ROWS_COUNT:
                insert_from_top = True
                break
            # if version or very beginning message is found then file is ok,
            # prepend line_to_insert before found line
            if version_regex.search(line) or beginning_message_regex.search(line):
                matched_version_line = i
                break

        if matched_version_line:
            lines.insert(matched_version_line, f"{line_to_insert}\n")
            should_update_file = True
        elif insert_from_top:
            line_to_insert = f"{CHANGELOG_TITLE}\n\n{line_to_insert}\n"
            lines.insert(0, line_to_insert)
            should_update_file = True

        if should_update_file:
            with open(file_path, 'w') as file:
                file.writelines(lines)
            logger.info('CHANGELOG.md file was updated.')
        else:
            logger.info('CHANGELOG.md file was not updated as no match is found.')
    except Exception as e:
        raise RuntimeError(f'Error in function insert_content_before_match_or_from_top: {e}')


def handle_release_notes(release_file_path, pr_number, build_number, repo_name, jira_ticket) -> list:
    """
    Create release notes file with commits after last tag. and returning the commits
    :param release_file_path:
    :param pr_number:
    :param build_number:
    :param repo_name:
    :param jira_ticket:
    :return: list of commits
    """
    try:
        latest_git_tag = get_latest_git_tag(repo_name)
        commits = get_commits_after_last_tag(repo_name=repo_name, tag=latest_git_tag)
        content = RELEASE_NOTES_VERY_BEGINNING_TEXT
        logger.info(f"last tag: {latest_git_tag}")
        if commits:
            logger.info(f'Creating release items from commits after {latest_git_tag} latest tag.')
            release_items = format_items(commits, '', '')

            release_items_str = '\n'.join(release_items).rstrip('\n')
            date = datetime.now().strftime(RELEASE_DATE_FORMAT)
            footer = get_footer(pr_number, repo_name)
            content = f"""\
{release_items_str}\n
build {build_number} from github actions on {date}:
{footer}
by jira {JIRA_HOST}/browse/{jira_ticket}"""

        with open(release_file_path, 'w', encoding="utf-8") as f:
            f.write(content)
        logger.info(f'{RELEASE_TMP_FILE_NAME} file was created with new content.')

        return commits
    except Exception as e:
        raise RuntimeError(f'Error in function handle_release_notes: {e}')


def handle_changelog(changelog_file_path, changelog_items: [str], jira_ticket, repo_name, tags, pr_number):
    try:
        jira_ticket_info = get_jira_ticket_info(jira_ticket)

        jira_changelog_description = get_changelog_description_from_jira(jira_ticket_info).replace('NEW_LINE_DEL', "\n")
        changelog_file_exists = check_file_exists(changelog_file_path)
        header = get_changelog_header(tags)
        description_items_header = CHANGELOG_DESCRIPTION_ITEM_HEADER # Added

        description_items = []
        empty_changelog_items = True

        if len(changelog_items):
            logger.info(f'Creating changelog items from commits.')
            description_items = changelog_items
            empty_changelog_items = False

        if jira_changelog_description:
            logger.info(f'Creating changelog items from jira attribute.')
            description_items = [jira_changelog_description]
            empty_changelog_items = False

        # if changelog_file_exists:
        #     empty_changelog_items = False

        if empty_changelog_items:
            if changelog_file_exists:
                description_items = format_items([CHANGELOG_NO_RELEASE_NOTES_TEXT], prefix='', suffix='')
                description_items_header = ''
            else:
                description_items = format_items([CHANGELOG_VERY_BEGINNING_TEXT], prefix='### ', suffix='')
        else:
            description_items = format_items(description_items)

        description_str = '\n'.join(description_items).rstrip('\n')
        new_content = f"""\
{header}
{description_items_header}
{description_str}"""

        if jira_changelog_description:
            footer = get_footer(pr_number, repo_name)
            new_content = f"{new_content}\n<br />\n{footer}"

        if changelog_file_exists:
            logger.info("CHANGELOG.md exists. Adding release notes to CHANGELOG.md")
            new_content = f"{new_content}\n\n"
            insert_content_before_match_or_from_top(changelog_file_path, new_content)
        else:
            # If no tags/commits, no jira changelog description and no CHANGELOG.md file exists
            # create the file with first static message
            if empty_changelog_items:
                logger.info('CHANGELOG.md file was created with very first content.')
                new_content = description_str
            else:
                logger.info('CHANGELOG.md file was created with new content.')

            new_content = f"{CHANGELOG_TITLE}\n\n{new_content}"
            with open(changelog_file_path, 'w', encoding="utf-8") as f:
                f.write(new_content)
    except Exception as e:
        raise RuntimeError(f'Error in function handle_changelog: {e}')


def get_changelog_header(tags: [str]) -> str:
    try:
        if not len(tags):
            return ''

        date = datetime.today().strftime(CHANGELOG_DATE_FORMAT)
        tags_str = ''

        tags_str = CHANGELOG_TAG_SEPARATOR.join(tags).rstrip(CHANGELOG_TAG_SEPARATOR)

        header = CHANGELOG_HEADER_TEMPLATE \
            .replace('%PREFIX%', CHANGELOG_HEADER_PREFIX) \
            .replace('%SUFFIX%', CHANGELOG_HEADER_SUFFIX) \
            .replace('%TAGS%', tags_str) \
            .replace('%MID_SEPARATOR%', CHANGELOG_HEADER_MID_SEPARATOR) \
            .replace('%DATE%', date)

        return header
    except Exception as e:
        raise RuntimeError(f'Error in function get_changelog_header: {e}')


def format_items(items: [str], prefix=CHANGELOG_ITEM_PREFIX, suffix=CHANGELOG_ITEM_SUFFIX) -> [str]:
    return [f'{prefix}{item}{suffix}' for item in items]


def get_changelog_description_from_jira(jira_ticket_info: JiraTicket) -> str:
    try:
        jira_changelog_description = ""

        if jira_ticket_info.fields.changelog_description:
            jira_changelog_description = jira_ticket_info.fields.changelog_description

        return jira_changelog_description
    except Exception as e:
        raise RuntimeError(f'Error in function get_changelog_description_from_jira: {e}')


def get_footer(pr_number, github_repository):
    return f'merged from PR [#{pr_number}]({GH_URL}/{github_repository}/pull/{pr_number})'


def get_latest_git_tag(github_repository):
    try:
        git_tags = get_tags(github_repository)
        if not git_tags:
            return False

        return git_tags[0]
    except Exception as e:
        raise RuntimeError(f'Error in function get_latest_git_tag: {e}')


def main():
    try:
        logger.info_green_bg('Starting changelog step.')
        ms_tag_version = os.getenv(MS_TAG_VERSION)
        env_file_version = os.getenv(ENV_FILE_VERSION)
        jira_ticket = get_required_env_var(JIRA_TICKET)
        repo_name = get_required_env_var(GITHUB_REPOSITORY)
        pr_number = get_required_env_var(PR_NUMBER)
        build_number = get_required_env_var(GITHUB_RUN_NUMBER)

        tags = [ms_tag_version, env_file_version]
        tags_list = [tag for tag in tags if tag]
        if not tags_list:
            exit_on_error_and_write_summary(f'Either ms_version or env_file_version should be provided.')

        logger.info_green(f'Will create changelog for version {ms_tag_version} and env file version {env_file_version}.')
        release_items = handle_release_notes(RELEASE_TMP_FILE_NAME, pr_number, build_number, repo_name, jira_ticket)
        logger.info_green('Release notes were created successfully.')
        logger.info_green('Will create changelog now:')

        handle_changelog(CHANGELOG_FILE_NAME, release_items, jira_ticket, repo_name, tags_list, pr_number)
        logger.info_green('Changelog was created successfully.')
        # temp file produced during this step that should be deleted after git tag in next steps
        write_github_env(RELEASE_TMP_FILE_NAME, RELEASE_TAGS_FILE)
    except Exception as e:
        exit_on_error_and_write_summary(f'Error during changelog step. Reason: {e}')


if __name__ == "__main__":
    main()
