#!/bin/python3
"""
zrok cloud docker entrypoint
"""
import os
import sys
import subprocess
import json
import yaml
from jinja2 import Environment, FileSystemLoader

def cat_file(cat_filename):
    """Utility function to print the contents of a file using the 'cat' command."""
    try:
        subprocess.run(['cat', cat_filename], check=True)
    except FileNotFoundError:
        print("Error: The 'cat' command was not found. Ensure it's in your system's PATH.")
    except subprocess.CalledProcessError as callerror:
        print(f"An error occurred: {callerror}")

zrok_command = os.environ.get('ZROK_COMMAND','ctrl')

# Command Flags
cmd_flags = os.environ.get(
    "ZROK_CMD_FLAGS",'')

print("Building zrok config...")
#################################################################################
# ctrl
#################################################################################
if zrok_command == 'ctrl':

    # Config & Template
    zrok_config_template = os.environ.get(
        "ZROK_CONFIG_TEMPLATE",
        "/home/zrok/ctrl.yml.j2")
    zrok_config_path = os.environ.get(
        "ZROK_CONFIG_PATH",
        "/home/zrok/config.yml")

    # Config Version
    config_version = int(os.environ.get(
        "ZROK_CONFIG_VERSION",
        4))

    # Admin
    admin_secret = os.environ.get(
        "ZROK_ADMIN_SECRET")
    admin_tou_link = os.environ.get(
        "ZROK_ADMIN_TOU_LINK",
        '<a href="https://netfoundry.io/legal-items/" target="_">Terms and Conditions</a>')

    # Endpoint
    endpoint_host = os.environ.get(
        "ZROK_ENDPOINT_HOST",
        "0.0.0.0")
    endpoint_port = int(os.environ.get(
        "ZROK_ENDPOINT_PORT",
        8080))

    # Email
    email_host = os.environ.get(
        "ZROK_EMAIL_HOST",
        'email-smtp.us-east-1.amazonaws.com')
    email_port = int(os.environ.get(
        "ZROK_EMAIL_PORT",
        587))
    email_username = os.environ.get(
        "ZROK_EMAIL_USERNAME")
    email_password = os.environ.get(
        "ZROK_EMAIL_PASSWORD")
    email_from_address = os.environ.get(
        "ZROK_EMAIL_FROM_ADDRESS",
        "ziggy@zrok.io")

    # Invites
    invites_open = os.environ.get(
        "ZROK_INVITES_OPEN",
        "true")
    token_contact = os.environ.get(
        "ZROK_TOKEN_CONTACT","invite@zrok.io")

    # Limits
    limits_environments = int(os.environ.get(
        "ZROK_LIMITS_ENVIRONMENTS",
        -1))
    limits_shares = int(os.environ.get(
        "ZROK_LIMITS_SHARES",
        -1))
    limits_reserved_shares = int(os.environ.get(
        "ZROK_LIMITS_RESERVED_SHARES",
        -1))
    limits_unique_names = int(os.environ.get(
        "ZROK_LIMITS_UNIQUE_NAMES",
        -1))
    limits_share_frontends = int(os.environ.get(
        "ZROK_LIMITS_SHARE_FRONTENDS",
        -1))

    # Limits - Bandwidth
    limits_bandwidth_period = os.environ.get(
        "ZROK_LIMITS_BANDWIDTH_PERIOD",
        "24h")
    limits_bandwidth_warning_rx = int(os.environ.get(
        "ZROK_LIMITS_BANDWIDTH_WARNING_RX",
        -1))
    limits_bandwidth_warning_tx = int(os.environ.get(
        "ZROK_LIMITS_BANDWIDTH_WARNING_TX",
        -1))
    limits_bandwidth_warning_total = int(os.environ.get(
        "ZROK_LIMITS_BANDWIDTH_WARNING_TOTAL",
        7500000000))
    limits_bandwidth_limit_rx = int(os.environ.get(
        "ZROK_LIMITS_BANDWIDTH_LIMIT_RX",
        -1))
    limits_bandwidth_limit_tx = int(os.environ.get(
        "ZROK_LIMITS_BANDWIDTH_PER_ACCOUNT_LIMIT_TX",
        -1))
    limits_bandwidth_limit_total = int(os.environ.get(
        "ZROK_LIMITS_BANDWIDTH_LIMIT_TOTAL",
        10000000000))


    limits_enforcing = os.environ.get(
        "ZROK_LIMITS_ENFORCING",
        "true")
    limits_cycle = os.environ.get(
        "ZROK_LIMITS_CYCLE",
        "15m")

    # Maintenance
    maintenance_registration_expiration_timeout = os.environ.get(
        "ZROK_MAINTENANCE_REGISTRATION_EXPIRATION_TIMEOUT",
        "24h")
    maintenance_registration_check_frequency = os.environ.get(
        "ZROK_MAINTENANCE_REGISTRATION_CHECK_FREQUENCY",
        "1h")
    maintenance_registration_batch_limit = int(os.environ.get(
        "ZROK_MAINTENANCE_REGISTRATION_BATCH_LIMIT",
        500))
    maintenance_reset_password_expiration_timeout = os.environ.get(
        "ZROK_MAINTENANCE_RESET_PASSWORD_EXPIRATION_TIMEOUT",
        "15m")
    maintenance_reset_password_check_frequency = os.environ.get(
        "ZROK_MAINTENANCE_RESET_PASSWORD_CHECK_FREQUENCY",
        "15m")
    maintenance_reset_password_batch_limit = int(os.environ.get(
        "ZROK_MAINTENANCE_RESET_PASSWORD_BATCH_LIMIT",
        500))

    # Metrics
    metrics_agent_source_type = os.environ.get(
        "ZROK_METRICS_AGENT_SOURCE_TYPE","amqpSource")
    metrics_agent_source_url = os.environ.get(
        "ZROK_METRICS_AGENT_SOURCE_URL")
    metrics_agent_source_queue_name = os.environ.get(
        "ZROK_METRICS_AGENT_SOURCE_QUEUE_NAME","events")
    metrics_influx_url = os.environ.get(
        "ZROK_METRICS_INFLUX_URL")
    metrics_influx_bucket = os.environ.get(
        "ZROK_METRICS_INFLUX_BUCKET","zrok")
    metrics_influx_org = os.environ.get(
        "ZROK_METRICS_INFLUX_ORG","zrok")
    metrics_influx_token = os.environ.get(
        "ZROK_METRICS_INFLUX_TOKEN")

    # Registration
    registration_registration_url_template = os.environ.get(
        "ZROK_REGISTRATION_REGISTRATION_URL_TEMPLATE","https://api.zrok.io/register")
    # Reset Password
    reset_password_reset_url_template = os.environ.get(
        "ZROK_RESET_PASSWORD_RESET_URL_TEMPLATE","https://api.zrok.io/resetPassword")

    # Store
    store_path = os.environ.get(
        "ZROK_STORE_PATH")
    store_type = os.environ.get(
        "ZROK_STORE_TYPE","postgres")
    store_enable_locking = os.environ.get(
        "ZROK_STORE_ENABLE_LOCKING",
        "true"
    )
    store_disable_auto_migration = os.environ.get(
        "ZROK_STORE_DISABLE_AUTO_MIGRATION",
        "true"
    )

    # Ziti
    ziti_api_endpoint = os.environ.get(
        "ZROK_ZITI_API_ENDPOINT")
    ziti_username = os.environ.get(
        "ZROK_ZITI_USERNAME")
    ziti_password = os.environ.get(
        "ZROK_ZITI_PASSWORD")

    # dynamic proxy
    dynamic_proxy_controller_amqp_url = os.environ.get(
        "ZROK_DYNAMIC_PROXY_CONTROLLER_AMQP_URL"
    )
    dynamic_proxy_controller_identity_json = os.environ.get(
        "ZROK_DYNAMIC_PROXY_CONTROLLER_ZITI_IDENTITY_JSON")

    dynamic_proxy_controller_enabled = False
    if dynamic_proxy_controller_identity_json is not None:
        dynamic_proxy_controller_enabled = True
    dynamic_proxy_controller_identity_path = os.environ.get(
        "ZROK_DYNAMIC_PROXY_CONTROLLER_IDENTITY_PATH",
        "/home/zrok/.zrok/identities/dynamicproxy.json")

    # Names
    names_disable_share_token_profanity_check = os.environ.get(
        "ZROK_NAMES_DISABLE_SHARE_TOKEN_PROFANITY_CHECK")
    names_disable_namespace_name_profanity_check = os.environ.get(
        "ZROK_NAMES_DISABLE_NAMESPACE_NAME_PROFANITY_CHECK")

    # Agent Controller
    agent_controller_ziti_id = os.environ.get(
        "ZROK_AGENT_CONTROLLER_ZITI_ID")
    agent_controller_identity_json = os.environ.get(
        "ZROK_AGENT_CONTROLLER_ZITI_IDENTITY_JSON")
    agent_controller_identity_path = os.environ.get(
        "ZROK_AGENT_CONTROLLER_IDENTITY_PATH",
        "/home/zrok/.zrok/identities/agentremoting.json")

    # Create the configuration dictionary
    config = {
        'config_version': config_version,
        'admin': {
            'secrets': [admin_secret],
            'tou_link': admin_tou_link
        },
        'endpoint': {
            'host': endpoint_host,
            'port': endpoint_port
        },
        'dynamic_proxy_controller': {
            'enabled': dynamic_proxy_controller_enabled,
            'identity_path':   dynamic_proxy_controller_identity_path,
            'amqp_publisher': {
                'url': dynamic_proxy_controller_amqp_url
            }

        },
        'email': {
            'host': email_host,
            'port': email_port,
            'username': email_username,
            'password': email_password,
            'from_address': email_from_address
        },
        'invites': {
            'invites_open': invites_open,
            'token_contact': token_contact
        },
        'limits': {
            'environments': limits_environments,
            'shares': limits_shares,
            'reserved_shares': limits_reserved_shares,
            'unique_names': limits_unique_names,
            'share_frontends': limits_share_frontends,
            'bandwidth': {

                'period': limits_bandwidth_period,
                'warning': {
                    'rx': limits_bandwidth_warning_rx,
                    'tx': limits_bandwidth_warning_tx,
                    'total': limits_bandwidth_warning_total
                },
                'limit': {
                    'rx': limits_bandwidth_limit_rx,
                    'tx': limits_bandwidth_limit_tx,
                    'total': limits_bandwidth_limit_total
                }

            },
            'enforcing': limits_enforcing,
            'cycle': limits_cycle
        },
        'maintenance': {
            'registration': {
                'expiration_timeout': maintenance_registration_expiration_timeout,
                'check_frequency': maintenance_registration_check_frequency,
                'batch_limit': maintenance_registration_batch_limit
            },
            'reset_password': {
                'expiration_timeout': maintenance_reset_password_expiration_timeout,
                'check_frequency': maintenance_reset_password_check_frequency,
                'batch_limit': maintenance_reset_password_batch_limit
            }
        },
        'metrics': {
            'agent': {
                'source': {
                    'type': metrics_agent_source_type,
                    'url': metrics_agent_source_url,
                    'queue_name': metrics_agent_source_queue_name
                }
            },
            'influx': {
                'url': metrics_influx_url,
                'bucket': metrics_influx_bucket,
                'org': metrics_influx_org,
                'token': metrics_influx_token
            }
        },
        'registration': {
            'registration_url_template': registration_registration_url_template
        },
        'reset_password': {
            'reset_url_template': reset_password_reset_url_template
        },
        'store': {
            'path': store_path,
            'type': store_type,
            'enable_locking': store_enable_locking,
            'disable_auto_migration': store_disable_auto_migration
        },
        'ziti': {
            'api_endpoint': ziti_api_endpoint,
            'username': ziti_username,
            'password': ziti_password
        },
        'names': {
            'disable_share_token_profanity_check': names_disable_share_token_profanity_check,
            'disable_namespace_name_profanity_check': names_disable_namespace_name_profanity_check
        },
        'agent_controller': {
            'ziti_id': agent_controller_ziti_id,
            'identity_json': agent_controller_identity_json,
            'identity_path': agent_controller_identity_path
        }
    }

    # generate config
    try:
        path, filename = os.path.split(os.path.abspath(zrok_config_template))
        jinja_environment = Environment(loader=FileSystemLoader(path))
        jinja_template = jinja_environment.get_template(filename)
        rendered_template = jinja_template.render(
            config=config)
    except OSError:
        print("FAILED: rendering jinja template")
        sys.exit(1)

    # load rendered template using yaml to verify it's good format
    try:
        yaml_content = yaml.load(rendered_template, Loader=yaml.FullLoader)
    except yaml.YAMLError:
        print("FAILED: verifying yaml format")
        sys.exit(1)

    # write configuration file
    try:
        with open(zrok_config_path, "w", encoding='UTF-8') as config_file:
            yaml.dump(yaml_content, config_file, sort_keys=False)
    except OSError:
        print("FAILED: writing config file to disk")
        sys.exit(1)

    # dynamic_proxy_controller_identity_json
    if dynamic_proxy_controller_identity_json is not None:
        print("Writing dynamic proxy identity to file: " + dynamic_proxy_controller_identity_path)
        try:
            with open(dynamic_proxy_controller_identity_path,
                      "w",
                      encoding='UTF-8') as identity_file:
                identity_file.write(dynamic_proxy_controller_identity_json)

        except OSError:
            print("FAILED: writing ziti dynamic proxy identity file to disk")
            sys.exit(1)

        # print("DPX JSON")
        # cat_file(dynamic_proxy_controller_identity_path)

    # write remote agent identity file
    if agent_controller_identity_json is not None:
        try:
            with open(agent_controller_identity_path,
                      "w",
                      encoding='UTF-8') as agent_controller_identity_file:
                agent_controller_identity_file.write(agent_controller_identity_json)
        except OSError:
            print("FAILED: writing remote agent ziti identity file to disk")
            sys.exit(1)

    sub_command=["/usr/local/bin/zrok", "controller", zrok_config_path]
    # Add cmd_flags only if it's not empty
    if cmd_flags:
        sub_command.extend(cmd_flags.split())

    if os.environ.get("ZROK_DEBUG", "false").lower() == "true":
        sub_command.append("-v")
        cat_file(zrok_config_path)

    subprocess.run(sub_command, cwd="/home/zrok/", check=True)


#################################################################################
# http or dynamicProxy
#################################################################################
if zrok_command in ['http', 'dynamicProxy']:

    # ziti Identity
    ziti_identity_json = os.environ.get("ZROK_ZITI_IDENTITY_JSON","Forgot creds?")
    ziti_identity_name = os.environ.get("ZROK_ZITI_IDENTITY_NAME","public")
    ziti_identity_path = os.environ.get(
        "ZROK_ZITI_IDENTITY_PATH",
        "/home/zrok/.zrok/identities/" + str(ziti_identity_name) + ".json")
    # Config & Template
    zrok_config_template = os.environ.get(
        "ZROK_CONFIG_TEMPLATE",
        "/home/zrok/http-frontend.yml.j2")
    zrok_config_path = os.environ.get(
        "ZROK_CONFIG_PATH",
        "/home/zrok/http-frontend.yml")
    host_match = os.environ.get('ZROK_FRONTEND_HOST_MATCH','share.zrok.io')

    # access dynamicProxy
    frontend_token = os.environ.get('ZROK_FRONTEND_TOKEN')
    refresh_interval = os.environ.get('ZROK_REFRESH_INTERVAL', '1m')
    amqp_url = os.environ.get('ZROK_AMQP_URL')

    # interstitial
    zrok_interstitial = os.environ.get(
        "ZROK_INTERSTITIAL",
        "false")
    # Path to an external interstitial HTML file. Defaults to the copy bundled
    # in the image because the embedded interstitial in the zrok binary is
    # unreachable (upstream embeds 'intersititial.html' but reads
    # 'interstitial.html'). Set to "" to fall back to the (broken) embed.
    zrok_interstitial_html_path = os.environ.get(
        "ZROK_INTERSTITIAL_HTML_PATH",
        "/home/zrok/interstitial.html")

    # oauth Config
    zrok_bind_address = os.environ.get(
        "ZROK_OAUTH_BIND_ADDRESS",
        "0.0.0.0:8181")
    zrok_endpoint_url = os.environ.get(
        "ZROK_ENDPOINT_URL",
        "https://oauth.share.zrok.io")
    zrok_cookie_domain = os.environ.get(
        "ZROK_COOKIE_DOMAIN",
        "zrok.io")
    zrok_cookie_name = os.environ.get(
        "ZROK_COOKIE_NAME",
        "zrok-session")
    zrok_hash_key = os.environ.get(
        "ZROK_HASH_KEY")
    zrok_google_client_id = os.environ.get(
        "ZROK_GOOGLE_CLIENTID")
    zrok_google_client_secret = os.environ.get(
        "ZROK_GOOGLE_CLIENTSECRET")
    zrok_github_client_id = os.environ.get(
        "ZROK_GITHUB_CLIENTID")
    zrok_github_client_secret = os.environ.get(
        "ZROK_GITHUB_CLIENTSECRET")
    zrok_session_lifetime = os.environ.get(
        "ZROK_SESSION_LIFETIME",
        "6h")
    zrok_intermediate_lifetime = os.environ.get(
        "ZROK_INTERMEDIATE_LIFETIME",
        "5m")
    zrok_signing_key = os.environ.get(
        "ZROK_SIGNING_KEY")
    zrok_encryption_key = os.environ.get(
        "ZROK_ENCRYPTION_KEY")
    try:
        zrok_providers_json = json.loads(os.environ.get(
            "ZROK_PROVIDERS_JSON",
            "[]"))
    except json.JSONDecodeError:
        print("FAILED: parsing ZROK_PROVIDERS_JSON")
        sys.exit(1)

    zrok_404_template_path = os.environ.get(
        'ZROK_404_TEMPLATE_PATH',
        False)

    # generate config
    try:
        path, filename = os.path.split(os.path.abspath(zrok_config_template))
        jinja_environment = Environment(loader=FileSystemLoader(path))
        jinja_template = jinja_environment.get_template(filename)
        rendered_template = jinja_template.render(
            host_match=host_match,
            identity_name=ziti_identity_name,
            interstitial=zrok_interstitial,
            interstitial_html_path=zrok_interstitial_html_path,
            bind_address=zrok_bind_address,
            endpoint_url=zrok_endpoint_url,
            cookie_domain=zrok_cookie_domain,
            cookie_name=zrok_cookie_name,
            session_lifetime=zrok_session_lifetime,
            intermediate_lifetime=zrok_intermediate_lifetime,
            signing_key=zrok_signing_key,
            encryption_key=zrok_encryption_key,
            providers_json=zrok_providers_json,
            frontend_token=frontend_token,
            mapping_refresh_interval=refresh_interval,
            identity_path=ziti_identity_path,
            amqp_url=amqp_url,
            template_path=zrok_404_template_path
        )
    except OSError:
        print("FAILED: rendering jinja template")
        sys.exit(1)

    # load rendered template using yaml to verify it's good format
    try:
        yaml_content = yaml.load(rendered_template, Loader=yaml.FullLoader)
    except yaml.YAMLError:
        print("FAILED: verifying yaml format")
        sys.exit(1)

    # write configuration file
    try:
        with open(zrok_config_path, "w", encoding='UTF-8') as config_file:
            yaml.dump(yaml_content, config_file, sort_keys=False)

    except OSError:
        print("FAILED: writing config file to disk")
        sys.exit(1)

    # write identity file
    try:
        with open(ziti_identity_path, "w", encoding='UTF-8') as identity_file:
            identity_file.write(ziti_identity_json)
    except OSError:
        print("FAILED: writing ziti identity file to disk")
        sys.exit(1)

    # write in stub environment files so zrok think it's an enabled environment
    try:
        with open("/home/zrok/.zrok/metadata.json", "w", encoding='UTF-8') as metadata_file:
            metadata_file.write('{"v":"v0.4"}')
        with open("/home/zrok/.zrok/environment.json", "w", encoding='UTF-8') as environment_file:
            environment_file.write('{}')
    except OSError:
        print("FAILED: environment metadata files to disk")
        sys.exit(1)

    if zrok_command == 'http':
        sub_command=["/usr/local/bin/zrok", "access", "public", zrok_config_path]
    else:
        sub_command=["/usr/local/bin/zrok", "access", "dynamicProxy", zrok_config_path]

    # Add cmd_flags only if it's not empty
    if cmd_flags:
        sub_command.extend(cmd_flags.split())

    if os.environ.get("ZROK_DEBUG", "false").lower() == "true":
        sub_command.append("-v")
        cat_file(zrok_config_path)

    print("Starting zrok...")
    subprocess.run(sub_command, cwd="/home/zrok/", check=True)
