"""A workflow that terminates a router."""

import ipaddress
import json
import logging

from orchestrator.forms import FormPage
from orchestrator.forms.validators import Label
from orchestrator.targets import Target
from orchestrator.types import FormGenerator, SubscriptionLifecycle, UUIDstr
from orchestrator.utils.json import json_dumps
from orchestrator.workflow import StepList, conditional, done, init, step, workflow
from orchestrator.workflows.steps import (
    resync,
    set_status,
    store_process_subscription,
    unsync,
)
from orchestrator.workflows.utils import wrap_modify_initial_input_form

from gso.products.product_blocks.router import RouterVendor
from gso.products.product_types.router import Router
from gso.services import infoblox
from gso.services.netbox_client import NetboxClient
from gso.services.provisioning_proxy import execute_playbook, pp_interaction

logger = logging.getLogger(__name__)


def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator:
    """Let the operator decide whether to delete configuration on the router, and clear up :term:`IPAM` resources."""
    router = Router.from_subscription(subscription_id)

    class TerminateForm(FormPage):
        termination_label: Label = (
            "Please confirm whether configuration should get removed from the router, and whether IPAM resources should"
            " be released."  # type: ignore[assignment]
        )
        tt_number: str
        remove_configuration: bool = True
        clean_up_ipam: bool = True

    user_input = yield TerminateForm
    return user_input.dict() | {"router_is_nokia": router.router.vendor == RouterVendor.NOKIA}


@step("Deprovision loopback IPs from IPAM")
def deprovision_loopback_ips(subscription: Router) -> dict:
    """Clear up the loopback addresses from :term:`IPAM`."""
    infoblox.delete_host_by_ip(ipaddress.IPv4Address(subscription.router.router_lo_ipv4_address))

    return {"subscription": subscription}


@step("[DRY RUN] Remove configuration from router")
def remove_config_from_router_dry(
    subscription: Router, callback_route: str, process_id: UUIDstr, tt_number: str
) -> None:
    """Remove configuration from the router, first as a dry run."""
    extra_vars = {
        "wfo_router_json": json.loads(json_dumps(subscription)),
        "dry_run": True,
        "verb": "terminate",
        "commit_comment": f"GSO_PROCESS_ID: {process_id} - TT_NUMBER: {tt_number} - Terminating "
        f"{subscription.router.router_fqdn}",
    }

    execute_playbook(
        playbook_name="base_config.yaml",
        callback_route=callback_route,
        inventory=subscription.router.router_fqdn,
        extra_vars=extra_vars,
    )


@step("[FOR REAL] Remove configuration from router")
def remove_config_from_router_real(
    subscription: Router, callback_route: str, process_id: UUIDstr, tt_number: str
) -> None:
    """Remove configuration from the router."""
    extra_vars = {
        "wfo_router_json": json.loads(json_dumps(subscription)),
        "dry_run": False,
        "verb": "terminate",
        "commit_comment": f"GSO_PROCESS_ID: {process_id} - TT_NUMBER: {tt_number} - Terminating "
        f"{subscription.router.router_fqdn}",
    }

    execute_playbook(
        playbook_name="base_config.yaml",
        callback_route=callback_route,
        inventory=subscription.router.router_fqdn,
        extra_vars=extra_vars,
    )


@step("Remove Device from Netbox")
def remove_device_from_netbox(subscription: Router) -> dict[str, Router]:
    """Remove the device from Netbox."""
    NetboxClient().delete_device(subscription.router.router_fqdn)
    return {"subscription": subscription}


@workflow(
    "Terminate router",
    initial_input_form=wrap_modify_initial_input_form(initial_input_form_generator),
    target=Target.TERMINATE,
)
def terminate_router() -> StepList:
    """Terminate a router subscription.

    * Let the operator decide whether to delete :term:`IPAM` resources, and remove configuration from the router
    * Clear up :term:`IPAM` resources, if selected by the operator
    * Disable and delete configuration on the router, if selected by the operator
    * Mark the subscription as terminated in the service database
    """
    run_ipam_steps = conditional(lambda state: state["clean_up_ipam"])
    run_config_steps = conditional(lambda state: state["remove_configuration"])
    router_is_nokia = conditional(lambda state: state["router_is_nokia"])

    return (
        init
        >> store_process_subscription(Target.TERMINATE)
        >> unsync
        >> run_ipam_steps(deprovision_loopback_ips)
        >> run_config_steps(pp_interaction(remove_config_from_router_dry))
        >> run_config_steps(pp_interaction(remove_config_from_router_real))
        >> router_is_nokia(remove_device_from_netbox)
        >> set_status(SubscriptionLifecycle.TERMINATED)
        >> resync
        >> done
    )