# Generated by Django 3.2.19 on 2023-05-17 10:48

import pint

from django.core.exceptions import ValidationError
from django.db import migrations

import InvenTree.conversion


def update_template_units(apps, schema_editor):
    """Update the units for each parameter template:

    - Check if the units are valid
    - Attempt to convert to valid units (if possible)
    """

    PartParameterTemplate = apps.get_model('part', 'PartParameterTemplate')

    n_templates = PartParameterTemplate.objects.count()

    if n_templates == 0:
        # Escape early
        return

    ureg = InvenTree.conversion.get_unit_registry()

    n_converted = 0
    invalid_units = set()

    for template in PartParameterTemplate.objects.all():

        # Skip empty units
        if not template.units:
            continue

        # Override '%' units (which are invalid)
        if template.units == '%':
            template.units = 'percent'
            template.save()
            n_converted += 1
            continue

        # Test if unit is 'valid'
        try:
            ureg.Unit(template.units)
            continue
        except Exception:
            pass

        # Check a lower-case version
        try:
            ureg.Unit(template.units.lower())
            print(f"Found unit match: {template.units} -> {template.units.lower()}")
            template.units = template.units.lower()
            template.save()
            n_converted += 1
            continue
        except Exception:
            pass

        found = False

        # Attempt to convert to a valid unit
        # Look for capitalization issues (e.g. "Ohm" -> "ohm")
        for unit in ureg:
            if unit.lower() == template.units.lower():
                print(f"Found unit match: {template.units} -> {unit}")
                template.units = str(unit)
                template.save()
                n_converted += 1
                found = True
                break

        if not found:
            print(f"warning: Could not find unit match for {template.units}")
            invalid_units.add(template.units)

    print(f"Updated units for {n_templates} parameter templates")

    if n_converted > 0:
        print(f" - Converted {n_converted} units")

    if len(invalid_units) > 0:
        print(f" - Found {len(invalid_units)} invalid units:")

        for unit in invalid_units:
            print(f"   - {unit}")



def convert_to_numeric_value(value: str, units: str):
    """Convert a value (with units) to a numeric value.

    Defaults to zero if the value cannot be converted.
    """

    # Default value is null
    result = None

    if units:
        try:
            result = InvenTree.conversion.convert_physical_value(value, units)
            result = float(result.magnitude)
        except Exception:
            pass
    else:
        try:
            result = float(value)
        except Exception:
            pass

    return result


def update_parameter_values(apps, schema_editor):
    """Update the parameter values for all parts:

    - Calculate the 'data_numeric' value for each parameter
    - If the template has invalid units, we'll ignore
    """

    PartParameter = apps.get_model('part', 'PartParameter')

    n_params = PartParameter.objects.count()

    # Convert each parameter value to a the specified units
    for parameter in PartParameter.objects.all():
        try:
            parameter.data_numeric = convert_to_numeric_value(parameter.data, parameter.template.units)
            parameter.save()
        except Exception:
            pass

    if n_params > 0:
        print(f"Updated {n_params} parameter values")


class Migration(migrations.Migration):

    atomic = False

    dependencies = [
        ('part', '0108_auto_20230516_1334'),
    ]

    operations = [
        migrations.RunPython(
            update_template_units,
            reverse_code=migrations.RunPython.noop
        ),
        migrations.RunPython(
            update_parameter_values,
            reverse_code=migrations.RunPython.noop
        )
    ]
