"""
Netdata Unit converter tool.
"""

import csv
import sys

#
# Mapping Rules:
# * Map to pre-defined UCUM units as much as possible
# * Metrics that count things and can be expressed as "3 apples" in a sentence,
#   should have the singular thing to be counted in curly bracket. Like
#   "{apple}".
# * Other dimensionless values, like a state should map to "1". E.g. you
#   cannot say "1 status" or "0 bool", so no unit should be displayed after
#   the value.
#

#
# Mapping from existing (constituent) units to UCUM units
#
mapping = {
    "%": "%",
    "% of time working": "%",
    "Ah": "A.h",
    "Ampere": "A",
    "Amps": "A",
    "B": "By",
    "Celsius": "Cel",
    "Fahrenheit": "[degF]",
    "GiB": "GiBy",
    "Hz": "Hz",
    "Joule": "J",
    "KB": "KiBy",
    "KiB": "KiBy",
    "MHz": "MHz",
    "Mbps": "Mibit/s",
    "MB": "MiBy",
    "MiB": "MiBy",
    "Minute": "min",
    "Minutes": "min",
    "Percent": "%",
    "RPM": "{rotation}/min",
    "Status": "1",  # bit field, dimensionless but no suffix
    "V": "V",
    "Volts": "V",
    "Watt": "W",
    "Watts": "W",
    "Wh": "W.h",
    "active connections": "{connection}",
    "bool": "1",
    "boolean": "1",
    "byte": "By",
    "bytes": "By",
    "celsius": "Cel",
    "children": "{child}",
    "context switches": "{context switch}",
    "count": "1",
    "cpu time": "1",
    "current threads": "{thread}",
    "cycle count": "{cycle}",
    "dBm": "dB[mW]",
    "dests": "{destination}",
    "difference": "1",
    "directories": "{directory}",
    "exit status": "1",
    "entries": "{entry}",
    "expired": "{object}",
    "failed disks": "{disk}",
    "failed servers": "{server}",
    "flushes": "{flush}",
    "health servers": "{server}",
    "hours": "h",
    "is degraded": "1",
    "kilobit": "Kibit",
    "kilobits": "Kibit",
    "kilobyte": "KiBy",
    "kilobytes": "KiBy",
    "merged operations": "{operation}",
    "microseconds": "us",
    "microseconds lost": "us",
    "milisecondds": "ms",
    "miliseconds": "ms",
    "milliseconds": "ms",
    "misses": "{miss}",
    "ms": "ms",
    "ns": "ns",
    "num": "1",
    "nuked": "{object}",
    "number": "1",
    "octet": "By",
    "open files": "{file}",
    "open pipes": "{pipe}",
    "ops": "{operation}",
    "percent": "%",
    "percentage": "%",
    "pps": "{packet}/s",
    "prefetches": "{prefetch}",
    "processes": "{process}",
    "queries": "{query}",
    "replies": "{reply}",
    "retries": "{retry}",
    "s": "s",
    "searches": "{search}",
    "sec": "s",
    "seconds": "s",
    "status": "1",
    "switches": "{switch}",
    "temperature": "Cel",
    "total": "1",
    "unsynchronised blocks": "{block}",
    "value": "1",
    "z": "1",

    # From go.d.plugin
    "assemblies": "{assembly}",
    "classes": "{class}",
    "conns": "{connection}",
    "difficulty": "1",
    "dispatches": "{dispatch}",
    "fetches": "{fetch}",
    "gpa": "{modification}",
    "hmode": "1",
    "indexes": "{index}",
    "log2": "1",
    "mac addresses": "{mac address}",
    "message bacthes": "{batch}",
    "message batches": "{batch}",
    "millicpu": "m{cpu}",
    "pgfaults": "{page fault}",
    "pmode": "1",
    "ppm": "[ppm]",
    "publishes": "{publish}",
    "queue_length": "1",
    "queue_processes": "{queue process}",
    "queued_size": "1",
    "ratio": "1",
    "running_containers": "{container}",
    "running_pod": "{pod}",
    "running_pods": "{pod}",
    "state": "1",
    "stratum": "1",
    "token_requests": "{request}",
    "us": "us",
    "watches": "{watch}",
    "xid": "1",
}

#
# Units for specific metric names
#
overrides = {
    "varnish.threads_total": "{thread}",
    "dovecot.sessions": "{session}",
    "dovecot.faults": "{page fault}",
    "dovecot.logins": "{login}",
    "dovecot.lookup": "{lookup}/s",
    "mem.pgfaults": "{page fault}/s",
    "system.pgfaults": "{page fault}/s",
    "smartd_log.erase_fail_count": "{erase}",
    "smartd_log.program_fail_count": "{program}",
    "mysql.thread_cache_misses": "{thread}",
    "mysql.galera_cluster_weight": "1",
    "hdfs.load": "{concurrent file accesses}",
    "docker.container_writeable_layer_size": "By",
}


def convert_unit(metric_name: str, unit: str) -> str:
    """
    Map an old unit to a UCUM-conforming one.

    1. If the metric name exists in `overrides`, use that.
    2. Otherwise:
      - Split the unit in components separated by a slash
      - Strip the whitespace.
    3. For each part:
      - Check if the unit appears in `mapping` and then use that.
      - Otherwise:
        - Strip a final s to make it singular
        - Put in between curly braces.
        - Lowercase
    4. Join parts with a slash.
    """
    try:
        result = overrides[metric_name]
    except KeyError:
        parts = [part.strip() for part in unit.split("/")]
        result_parts = []
        for part in parts:
            try:
                part = mapping[part]
            except KeyError:
                if part[-1] == "s":
                    part = part[:-1]
                part = f"{{{part}}}".lower()
            result_parts.append(part)

        result = "/".join(result_parts)
    return result


def main() -> None:
    """
    Open file as CSV and print out rows with converted units for verification.

    The CSV is expected to follow the metrics.csv format.
    """
    with open(sys.argv[1], encoding="utf-8") as csvfile:
        rows = csv.reader(csvfile, delimiter=",", quotechar='"')
        for row in rows:
            if row[0] == "metric" and row[3] == "unit":
                continue
            print("-----")
            print(f"Metric: {row[0]}")
            print(f"  Scope: {row[1]}")
            print(f"  Dimensions: {row[2]}")
            old_unit = row[3]
            converted_unit = convert_unit(row[0], old_unit)
            print(f"  Unit: {old_unit} ----> {converted_unit}")
            print(f"  Description: {row[4]}")
            print(f"  Chart type: {row[5]}")
            print(f"  Labels: {row[6]}")
            print(f"  Plugin: {row[7]}")
            print(f"  Module: {row[8]}")


if __name__ == "__main__":
    main()
