#!/usr/bin/python3

import urllib.request, urllib.error
import re
import sys
import yaml


repos = {
    "https://github.com/OCA/account-financial-reporting": [
        "account_tax_balance"
    ],
    "https://github.com/OCA/account-financial-tools": [
        "account_lock_date_update",
        "account_move_name_sequence",
        "account_reconcile_show_boolean"
    ],
    "https://github.com/OCA/account-invoicing": [
        "sale_timesheet_invoice_description"
    ],
    "https://github.com/OCA/account-reconcile": [
        "account_reconciliation_widget"
    ],
    "https://github.com/OCA/bank-statement-import": [
        "account_statement_import",
        "account_statement_import_ofx"
    ],
    "https://github.com/OCA/crm": [
        "crm_stage_probability"
    ],
    "https://github.com/OCA/partner-contact": [
        "partner_disable_gravatar",
        "partner_firstname"
    ],
    "https://github.com/OCA/project": [
        "project_category",
        "project_status",
        "project_task_default_stage",
        "project_template",
        "project_timeline"
    ],
    "https://github.com/OCA/server-auth": [
        "password_security"
    ],
    "https://github.com/OCA/server-brand": [
        "disable_odoo_online",
        "remove_odoo_enterprise"
    ],
    "https://github.com/OCA/server-tools": [
        "base_search_fuzzy",
        "module_change_auto_install"
    ],
    "https://github.com/OCA/server-ux": [
        "base_technical_features",
        "date_range",
        "mass_editing"
    ],
    "https://github.com/OCA/social": [
        "base_search_mail_content",
        "mail_debrand",
        "mail_tracking"
    ],
    "https://github.com/OCA/web": [
        "web_environment_ribbon",
        "web_responsive",
        "web_no_bubble",
        "web_timeline"
    ]
}


class Style:
    """Set of console display styles.

    """
    black = '\033[30m'
    red = '\033[31m'
    green = '\033[32m'
    yellow = '\033[33m'
    blue = '\033[34m'
    magenta = '\033[35m'
    cyan = '\033[36m'
    lightgray = '\033[37m'
    default = '\033[39m'
    bold = '\033[1m'
    normal = '\033[0m'


def print_err(message):
    print(f"{Style.red}{Style.bold}err: {message}{Style.normal}"
          f"{Style.default}", file=sys.stderr)


def check_pr(repo, module):
    conn = urllib.request.urlopen(
        f"{repo}/pulls?q=is%3Apr+is%3Aopen+%5B{version}%5D+mig+{module}")
    html = conn.read().decode('utf-8')
    result = re.search(rf"{repo[18:]}/pull/\d+", html)
    if result:
        return f"https://github.com{result.group()}"
    return None

def load_repos(host_file):
    with open(host_file, 'r', encoding='utf8') as file:
        host = yaml.safe_load(file)
        for instance in host['odoo_nonprod_instances']:
            for repo in instance['custom_modules_oca']:
                repo_url = f"https://github.com/OCA/{repo['repo']}"
                if repos.get(repo_url):
                    repos[repo_url].extend(repo['modules'])
                else:
                    repos[repo_url] = repo['modules']
            for repo in instance['custom_modules']:
                repos[f"https://sources.le-filament.com/lefilament/{repo}"] = None

def print_help():
    print(f"usage:\n  {sys.argv[0]} <version> [hostvars file]\n"
          "options:\n  <version>        Odoo version\n"
          "  [hostvars file]  Ansible hostvars file to also check modules of"
          " this host")


def main():
    if len(sys.argv) >= 2:
        version = sys.argv[1]
    else:
        print_err("missing version argument (e.g.: '16.0')")
        sys.exit(1)
    if sys.argv[1] == '-h' or sys.argv[1] == '--help':
        print_help()
        sys.exit(0)
    if len(sys.argv) == 3:
        load_repos(sys.argv[2])

    print(f"{Style.blue}💡 Checking availability Odoo v{version} "
          f"modules...{Style.default}\n")

    module_count = 0
    available_module_count = 0
    pr_count = 0
    for repo, modules in repos.items():
        tree = "tree"
        if 'github.com' not in repo:
            tree = '-/tree'
        repo_name = repo.split('/')[-1]

        for module in modules or []:
            module_count += 1
            try:
                conn = urllib.request.urlopen(f"{repo}/{tree}/"
                                              f"{version}/{module}")
            except urllib.error.HTTPError as err:
                if err.code == 404:
                    pr_url = check_pr(repo, module)
                    if pr_url:
                        pr_count += 1
                        print(f"{Style.red}❌ Not ready: {module} "
                              f"(in {repo_name}){Style.default} "
                              f"PR at {pr_url}")
                    else:
                        print(f"{Style.red}❌ Not ready: {module} "
                              f"(in {repo_name}){Style.default}")
                else:
                    print_err(f"HTTP error code {err.code} "
                              f"for {module} (in {repo})")
            except urllib.error.URLError as err:
                print_err(f"HTTP error '{err.reason}' for {module} "
                          f"(in {repo_name})")
            else:
                available_module_count += 1
                print(f"{Style.green}✅ Available: {module} "
                      f"(in {repo_name}){Style.default}")

        if not modules:
            module_count += 1
            try:
                conn = urllib.request.urlopen(f"{repo}/{tree}/{version}/")
            except urllib.error.HTTPError as err:
                if err.code == 404:
                    print(f"{Style.red}❌ Not already: {repo_name}"
                          f"{Style.default}")
                else:
                    print_err(f"HTTP error code {err.code} for {repo_name}")
            except urllib.error.URLError as err:
                print_err(f"HTTP error '{err.reason}' for {repo_name}")
            else:
                available_module_count += 1
                print(f"{Style.green}✅ Available:  {repo_name}"
                      f"{Style.default}")


    if available_module_count < module_count:
        print(f"\n{Style.bold}Result: {Style.green}{available_module_count}"
              f"{Style.default} / {module_count} modules are available. "
              f"There are {pr_count} opened pull requests.{Style.normal}")
    else:
        print(f"\n{Style.green}{Style.bold}Result: all {module_count} modules "
              f"are available. It's ready to go!{Style.normal}{Style.default}")


if __name__ == '__main__':
    main()