from dynatrace import Dynatrace
from dynatrace.http_client import TOO_MANY_REQUESTS_WAIT
from rich.progress import track
from rich import print

from typing import Optional, List, Dict, Union
from urllib.parse import urlparse

from .models import CustomDBQuery
from .utils import slugify

TIMEFRAME = "now-1y"


def lookup_columns_for_query(dt: Dynatrace, query_name: str):
    column_names = []
    query = f'custom.db.query:filter(eq(query_name,"{query_name}")):splitBy(column,query_name)'
    metric_series_collections = dt.metrics.query(
        query, time_from=TIMEFRAME, resolution="Inf"
    )
    for collection in metric_series_collections:
        for series in collection.data:
            column = series.dimension_map.get("column")
            if column:
                column_names.append(column)

    return column_names


def queries_from_ef1_config(properties: dict):
    properties.update(
        {
            "database_type": properties["database_type"],
            "group_name": properties["group_name"],
            "database_host": properties["database_host"],
            "database_name": properties["database_name"],
            "database_username": properties["database_username"],
            "custom_device_name": properties["custom_device_name"],
        }
    )

    configured_queries: List[CustomDBQuery] = []
    query_index = 1
    while query_index < 11:
        if properties[f"query_{query_index}_value"]:
            configured_queries.append(
                CustomDBQuery(
                    properties[f"query_{query_index}_name"],
                    properties[f"query_{query_index}_schedule"],
                    properties[f"query_{query_index}_value"],
                    properties[f"query_{query_index}_value_columns"],
                    properties[f"query_{query_index}_dimension_columns"],
                    properties[f"query_{query_index}_extra_dimensions"],
                )
            )
        query_index += 1
    return configured_queries


def ef2_datasource(db_type: str):
    if db_type == "Oracle":
        return "sqlOracle"
    elif db_type == "DB2":
        return "sqlDb2"
    elif db_type == "SQL Server":
        return "sqlServer"
    elif db_type == "MySQL":
        return "sqlMySql"
    elif db_type == "PostgreSQL":
        return "sqlPostgres"
    elif db_type == "SAP HANA":
        return "sqlHana"
    else:
        raise Exception(f"Unsupported database type: {db_type}")


def activation(endpoint_name: str, props: dict, credential_vault_id: str = None):

    database_type = props["database_type"]
    host = props.get("database_host") or None
    port = int(props.get("database_port")) or None
    database_name = props.get("database_name") or None
    database_username = props.get("database_username") or None
    oracle_listener_type = props.get("oracle_listener_type") or None
    jdbc_connection_string = props.get("jdbc_connection_string") or None

    endpoint = {}

    if jdbc_connection_string:
        if database_type not in ["Oracle, PostgreSQL", "SQL Server"]:
            raise Exception(f"{database_type} datasource does not currently support connection strings.")
        endpoint['useConnectionString'] = True
        endpoint['connectionString'] = jdbc_connection_string
        # try:
        #     # try parsing out some details from the connection string
        #     print(jdbc_connection_string)
        #     r = urlparse(jdbc_connection_string)
        #     print(r.scheme)
        # except Exception as e:
        #     print(f"Can't parse jdbc '{jdbc_connection_string}': {e}")
        endpoint['host'] = "SET-ME"
        endpoint['port'] = 1234
        endpoint['databaseName'] = "SET-ME"
    else:
        endpoint['host'] = host
        endpoint['port'] = port

    if credential_vault_id:
        endpoint['authentication'] = {
            "scheme": "basic",
            "useCredentialVault": True,
            "credentialVaultId": credential_vault_id
        }
    else:
        endpoint['authentication'] = {
            "scheme": "basic",
            "useCredentialVault": False,
            "username": database_username,
            "password": "changeme"
        }

    if database_type == "Oracle":
        endpoint['databaseIdentifier'] = "sid" if oracle_listener_type == "SID" else "serviceName"
        endpoint.update({"SID" if oracle_listener_type == "SID" else "serviceName": database_name})
    else:
        endpoint['databaseName'] = database_name

    activation_config = {
        "enabled": False,
        "description": f"Migrated extension from EF1 db queries config {endpoint_name}.",
        "version": "1.0.0",
        f"{ef2_datasource(props['database_type'])}Remote": {
            "endpoints": [endpoint]
        }
    }

    if database_type == "Oracle":
        activation_config[f"{ef2_datasource(props['database_type'])}Remote"].update({"licenseAccepted": True})

    return activation_config


class EF2SqlExtension:
    def __init__(
        self, dt: Dynatrace, endpoint_name: str, ef1_config_properties: Union[Dict, List], credential_vault_id = None, pre_cron = False
    ) -> None:
        

        if type(ef1_config_properties) == list:
            # have to merge queries from multiple configs into one
            queries = []
            db_type = ef2_datasource(ef1_config_properties[0]["database_type"])
            for conf_prop in ef1_config_properties:
                queries.extend(queries_from_ef1_config(conf_prop))
            self.activation_config = activation(endpoint_name, ef1_config_properties[0], credential_vault_id)
        else:
            db_type = ef2_datasource(ef1_config_properties["database_type"])
            queries = queries_from_ef1_config(ef1_config_properties)
            self.activation_config = activation(endpoint_name, ef1_config_properties, credential_vault_id)

        
        extension = {
            "name": f"custom:db.query.{slugify(endpoint_name)[:30] if len(slugify(endpoint_name)) > 30 else slugify(endpoint_name)}",
            "version": "1.0.0",
            "minDynatraceVersion": "1.301" if not pre_cron else "1.299",
            "author": {"name": "Dynatrace"},
            db_type: [],
        }

        group_number = 1
        subgroup_number = 0

        group = {
                "group": f"queries-{group_number}",
                "subgroups": []
            }

        for query in queries:
            if subgroup_number == 10:
                extension[db_type].append(group)
                group_number+=1
                group = {
                    "group": f"queries-{group_number}",
                    "subgroups": []
                }
            metric_columns = query.value_columns
            if not metric_columns:
                metric_columns = lookup_columns_for_query(dt, query.name)
            else:
                metric_columns = metric_columns

            safe_query_name = slugify(query.name)

            subgroup = {
                "subgroup": query.name,
                "query": query.value,
                "metrics": [],
                "dimensions": [{"key": "query_name", "value": f"const:{query.name}"}],
            }

            if not pre_cron:
                subgroup.update({"schedule": query.schedule})

            for column in metric_columns:
                subgroup["metrics"].append(
                    {
                        "key": f"{safe_query_name}.{slugify(column)}",
                        "value": f"col:{column}",
                        "type": "gauge",
                    }
                )

            for column in query.dimension_columns:
                subgroup['dimensions'].append(
                    {
                        "key": column.lower() ,
                        "value": f"col:{column}"
                    }
                )

            for dimension_pair in query.extra_dimensions:
                key, value = dimension_pair.split("=")
                subgroup['dimensions'].append(
                    {
                        "key": key.lower(),
                        "value": f"const:{value}"
                    }
                )

            subgroup_number += 1
            group['subgroups'].append(subgroup)


        extension[db_type].append(group)

        self.name = "custom:" + extension["name"].split(":")[1]
        self.version = extension["version"]
        self.dictionary = extension
