import click

from k4s.cli.state import CliState
from k4s.cli.target import resolve_k8s_target, kubeconfig_path_for
from k4s.cli.verbosity import apply_command_ui_overrides
from k4s.cli.errors import exit_with_error
from k4s.core.executor import Executor
from k4s.core.products import run_steps
from k4s.recipes.ingress_nginx.upgrade import build_upgrade_steps as build_ingress_nginx_upgrade_steps
from k4s.recipes.ingress_nginx.model import IngressNginxInstallPlan
from k4s.recipes.starburst_oci.upgrade import build_upgrade_steps as build_starburst_oci_upgrade_steps
from k4s.recipes.starburst_oci.install import build_plan_from_env as starburst_oci_plan_from_env
from k4s.recipes.starburst_oci.model import StarburstOciComponentInstallPlan
from k4s.recipes.starburst.upgrade import build_upgrade_steps as build_starburst_upgrade_steps
from k4s.recipes.starburst.install import build_plan_from_env as starburst_plan_from_env
from k4s.recipes.starburst.model import StarburstInstallPlan
from k4s.recipes.datafloem.upgrade import build_upgrade_steps as build_datafloem_upgrade_steps
from k4s.recipes.datafloem.install import build_plan_from_env as datafloem_plan_from_env
from k4s.recipes.datafloem.model import DatafloemInstallPlan


class OrderedUpgradeGroup(click.Group):
    """Upgrade subgroup with stable command order in --help."""

    _order = [
        "ingress-nginx",
        "hive",
        "ranger",
        "starburst",
        "cache",
        "datafloem",
    ]

    def list_commands(self, ctx):
        names = list(self.commands)
        ordered = [c for c in self._order if c in self.commands]
        rest = [c for c in names if c not in ordered]
        return ordered + rest

    def format_commands(self, ctx, formatter):
        """Render command list without per-command descriptions."""
        rows = []
        for subcommand in self.list_commands(ctx):
            cmd = self.get_command(ctx, subcommand)
            if cmd is None or getattr(cmd, "hidden", False):
                continue
            rows.append((subcommand, ""))
        if rows:
            with formatter.section("Commands"):
                formatter.write_dl(rows)


@click.group(cls=OrderedUpgradeGroup)
@click.pass_context
def upgrade(ctx):
    """Upgrade installed products."""
    pass


# ---------------------------------------------------------------------------
# ingress-nginx
# ---------------------------------------------------------------------------

@upgrade.command("ingress-nginx", short_help="")
@click.option("--context", "context_name", default=None, help="K8s context name. Uses current context if omitted.")
@click.option("--name", default="ingress-nginx", show_default=True, help="Helm release name.")
@click.option("--namespace", default=None, help="Kubernetes namespace (defaults to context namespace, then 'ingress-nginx').")
@click.option("--chart-version", default=None, help="Optional Helm chart version to pin.")
@click.option("--values-file", "values_files", type=click.Path(exists=True, dir_okay=False), multiple=True, help="Helm values YAML file (repeatable).")
@click.option("--set", "set_values", multiple=True, help="Helm --set overrides (repeatable).")
@click.option("--timeout", default="10m", show_default=True, help="Rollout timeout.")
@click.option("--force", is_flag=True, help="Delete pods immediately after upgrade.")
@click.option("--dry-run", is_flag=True, help="Print the plan without applying changes.")
@click.option("-q", "--quiet", is_flag=True, help="Only print final results and errors.")
@click.option("-v", "--verbose", count=True, help="Increase verbosity (-v logs, -vv debug).")
@click.pass_context
def upgrade_ingress_nginx(
    ctx, context_name, name, namespace, chart_version, values_files,
    set_values, timeout, force, dry_run, quiet, verbose,
):
    """Upgrade an existing ingress-nginx release via Helm."""
    state: CliState = ctx.obj["state"]
    apply_command_ui_overrides(ctx, state, quiet=quiet, verbose=verbose)
    ui = state.ui

    try:
        c = resolve_k8s_target(state, context_name)
        kubeconfig = kubeconfig_path_for(c)
        ns = namespace or c.namespace or "ingress-nginx"

        plan = IngressNginxInstallPlan(
            release_name=name,
            namespace=ns,
            kubeconfig_path=kubeconfig,
            kubectl_context=c.kubectl_context,
            chart_version=chart_version,
            values_files=list(values_files) if values_files else None,
            set_values=list(set_values) if set_values else None,
            timeout=timeout,
        )

        ex = Executor()
        steps = build_ingress_nginx_upgrade_steps(ui, ex, plan, force=force)
        if dry_run:
            run_steps(ui, steps, dry_run=True)
        else:
            with ex:
                run_steps(ui, steps, dry_run=False)

        if not dry_run:
            state.history.append(
                action="upgrade",
                product="ingress-nginx",
                context=c.name,
                params={"namespace": ns, "chart_version": chart_version, "release": name},
            )
            ui.success("ingress-nginx upgraded successfully.")
    except Exception as e:
        exit_with_error(ctx, ui, e, code=1)


# ---------------------------------------------------------------------------
# hive
# ---------------------------------------------------------------------------

@upgrade.command("hive", short_help="")
@click.option("--context", "context_name", default=None, help="K8s context name. Uses current context if omitted.")
@click.option("--name", default="hive", show_default=True, help="Helm release name.")
@click.option("--namespace", default=None, help="Kubernetes namespace (defaults to context namespace, then 'sep').")
@click.option("--chart-version", required=True, help="Helm chart version to upgrade to (e.g. 453.0.0).")
@click.option("--repo-username", default=None, help="Starburst Harbor username (or env K4S_STARBURST_REPO_USERNAME).")
@click.option("--repo-password", default=None, help="Starburst Harbor password (or env K4S_STARBURST_REPO_PASSWORD).")
@click.option("--values-file", "values_files", type=click.Path(exists=True, dir_okay=False), multiple=True, help="Helm values YAML file (repeatable).")
@click.option("--set", "set_values", multiple=True, help="Helm --set overrides (repeatable).")
@click.option("--timeout", default="20m", show_default=True, help="Rollout timeout.")
@click.option("--force", is_flag=True, help="Delete pods immediately after upgrade.")
@click.option("--dry-run", is_flag=True, help="Print the plan without applying changes.")
@click.option("-q", "--quiet", is_flag=True, help="Only print final results and errors.")
@click.option("-v", "--verbose", count=True, help="Increase verbosity (-v logs, -vv debug).")
@click.pass_context
def upgrade_hive(
    ctx, context_name, name, namespace, chart_version, repo_username, repo_password,
    values_files, set_values, timeout, force, dry_run, quiet, verbose,
):
    """Upgrade an existing Hive Metastore Service release via Helm (OCI chart)."""
    state: CliState = ctx.obj["state"]
    apply_command_ui_overrides(ctx, state, quiet=quiet, verbose=verbose)
    ui = state.ui

    try:
        c = resolve_k8s_target(state, context_name)
        kubeconfig = kubeconfig_path_for(c)
        ns = namespace or c.namespace or "sep"

        plan = StarburstOciComponentInstallPlan(
            component="hive",
            release_name=name,
            namespace=ns,
            kubeconfig_path=kubeconfig,
            kubectl_context=c.kubectl_context,
            oci_chart_ref="oci://harbor.starburstdata.net/starburstdata/charts/starburst-hive",
            chart_version=chart_version,
            repo_username=repo_username,
            repo_password=repo_password,
            values_files=list(values_files) if values_files else None,
            set_values=list(set_values) if set_values else None,
            timeout=timeout,
        )
        plan = starburst_oci_plan_from_env(plan)

        ex = Executor()
        steps = build_starburst_oci_upgrade_steps(ui, ex, plan, force=force)
        if dry_run:
            run_steps(ui, steps, dry_run=True)
        else:
            with ex:
                run_steps(ui, steps, dry_run=False)

        if not dry_run:
            state.history.append(
                action="upgrade",
                product="hive",
                context=c.name,
                params={"namespace": ns, "chart_version": chart_version, "release": name},
            )
            ui.success("Hive upgraded successfully.")
    except Exception as e:
        exit_with_error(ctx, ui, e, code=1)


# ---------------------------------------------------------------------------
# ranger
# ---------------------------------------------------------------------------

@upgrade.command("ranger", short_help="")
@click.option("--context", "context_name", default=None, help="K8s context name. Uses current context if omitted.")
@click.option("--name", default="ranger", show_default=True, help="Helm release name.")
@click.option("--namespace", default=None, help="Kubernetes namespace (defaults to context namespace, then 'sep').")
@click.option("--chart-version", required=True, help="Helm chart version to upgrade to (e.g. 453.0.0).")
@click.option("--repo-username", default=None, help="Starburst Harbor username (or env K4S_STARBURST_REPO_USERNAME).")
@click.option("--repo-password", default=None, help="Starburst Harbor password (or env K4S_STARBURST_REPO_PASSWORD).")
@click.option("--values-file", "values_files", type=click.Path(exists=True, dir_okay=False), multiple=True, help="Helm values YAML file (repeatable).")
@click.option("--set", "set_values", multiple=True, help="Helm --set overrides (repeatable).")
@click.option("--timeout", default="20m", show_default=True, help="Rollout timeout.")
@click.option("--force", is_flag=True, help="Delete pods immediately after upgrade.")
@click.option("--dry-run", is_flag=True, help="Print the plan without applying changes.")
@click.option("-q", "--quiet", is_flag=True, help="Only print final results and errors.")
@click.option("-v", "--verbose", count=True, help="Increase verbosity (-v logs, -vv debug).")
@click.pass_context
def upgrade_ranger(
    ctx, context_name, name, namespace, chart_version, repo_username, repo_password,
    values_files, set_values, timeout, force, dry_run, quiet, verbose,
):
    """Upgrade an existing Starburst Ranger release via Helm (OCI chart)."""
    state: CliState = ctx.obj["state"]
    apply_command_ui_overrides(ctx, state, quiet=quiet, verbose=verbose)
    ui = state.ui

    try:
        c = resolve_k8s_target(state, context_name)
        kubeconfig = kubeconfig_path_for(c)
        ns = namespace or c.namespace or "sep"

        plan = StarburstOciComponentInstallPlan(
            component="ranger",
            release_name=name,
            namespace=ns,
            kubeconfig_path=kubeconfig,
            kubectl_context=c.kubectl_context,
            oci_chart_ref="oci://harbor.starburstdata.net/starburstdata/charts/starburst-ranger",
            chart_version=chart_version,
            repo_username=repo_username,
            repo_password=repo_password,
            values_files=list(values_files) if values_files else None,
            set_values=list(set_values) if set_values else None,
            timeout=timeout,
        )
        plan = starburst_oci_plan_from_env(plan)

        ex = Executor()
        steps = build_starburst_oci_upgrade_steps(ui, ex, plan, force=force)
        if dry_run:
            run_steps(ui, steps, dry_run=True)
        else:
            with ex:
                run_steps(ui, steps, dry_run=False)

        if not dry_run:
            state.history.append(
                action="upgrade",
                product="ranger",
                context=c.name,
                params={"namespace": ns, "chart_version": chart_version, "release": name},
            )
            ui.success("Ranger upgraded successfully.")
    except Exception as e:
        exit_with_error(ctx, ui, e, code=1)


# ---------------------------------------------------------------------------
# starburst
# ---------------------------------------------------------------------------

@upgrade.command("starburst", short_help="")
@click.option("--context", "context_name", default=None, help="K8s context name. Uses current context if omitted.")
@click.option("--name", default="sep", show_default=True, help="Helm release name.")
@click.option("--namespace", default=None, help="Kubernetes namespace (defaults to context namespace, then 'sep').")
@click.option("--chart-version", required=True, help="Helm chart version to upgrade to (e.g. 453.0.0).")
@click.option("--repo-username", default=None, help="Starburst Harbor username (or env K4S_STARBURST_REPO_USERNAME).")
@click.option("--repo-password", default=None, help="Starburst Harbor password (or env K4S_STARBURST_REPO_PASSWORD).")
@click.option("--values-file", "values_files", type=click.Path(exists=True, dir_okay=False), multiple=True, help="Helm values YAML file (repeatable).")
@click.option("--set", "set_values", multiple=True, help="Helm --set overrides (repeatable).")
@click.option("--timeout", default="20m", show_default=True, help="Rollout timeout.")
@click.option("--environment", default=None, help="SEP environment (sets .environment).")
@click.option("--shared-secret", default=None, help="SEP shared secret (sets .sharedSecret).")
@click.option("--license-file", type=click.Path(exists=True, dir_okay=False), default=None, help="Path to starburstdata.license file (k4s creates/updates secret).")
@click.option("--license-secret-name", default=None, help="Existing or desired license secret name (default: starburst-license).")
@click.option("--registry-username", default=None, help="Harbor registry username (or env K4S_STARBURST_REGISTRY_USERNAME).")
@click.option("--registry-password", default=None, help="Harbor registry password (or env K4S_STARBURST_REGISTRY_PASSWORD).")
@click.option("--force", is_flag=True, help="Delete pods immediately after upgrade.")
@click.option("--dry-run", is_flag=True, help="Print the plan without applying changes.")
@click.option("-q", "--quiet", is_flag=True, help="Only print final results and errors.")
@click.option("-v", "--verbose", count=True, help="Increase verbosity (-v logs, -vv debug).")
@click.pass_context
def upgrade_starburst(
    ctx, context_name, name, namespace, chart_version, repo_username, repo_password,
    values_files, set_values, timeout, environment, shared_secret,
    license_file, license_secret_name, registry_username, registry_password,
    force, dry_run, quiet, verbose,
):
    """Upgrade an existing Starburst Enterprise release via Helm."""
    state: CliState = ctx.obj["state"]
    apply_command_ui_overrides(ctx, state, quiet=quiet, verbose=verbose)
    ui = state.ui

    try:
        c = resolve_k8s_target(state, context_name)
        kubeconfig = kubeconfig_path_for(c)
        ns = namespace or c.namespace or "sep"

        plan = StarburstInstallPlan(
            release_name=name,
            namespace=ns,
            kubeconfig_path=kubeconfig,
            kubectl_context=c.kubectl_context,
            chart_version=chart_version,
            repo_username=repo_username,
            repo_password=repo_password,
            values_files=list(values_files) if values_files else None,
            set_values=list(set_values) if set_values else None,
            environment=environment,
            shared_secret=shared_secret,
            license_file=license_file,
            license_secret_name=license_secret_name,
            registry_username=registry_username,
            registry_password=registry_password,
            timeout=timeout,
        )
        plan = starburst_plan_from_env(plan)

        ex = Executor()
        steps = build_starburst_upgrade_steps(ui, ex, plan, force=force)
        if dry_run:
            run_steps(ui, steps, dry_run=True)
        else:
            with ex:
                run_steps(ui, steps, dry_run=False)

        if not dry_run:
            state.history.append(
                action="upgrade",
                product="starburst",
                context=c.name,
                params={
                    "namespace": ns,
                    "chart_version": chart_version,
                    "release": name,
                },
            )
            ui.success("Starburst upgraded successfully.")
    except Exception as e:
        exit_with_error(ctx, ui, e, code=1)


# ---------------------------------------------------------------------------
# cache
# ---------------------------------------------------------------------------

@upgrade.command("cache", short_help="")
@click.option("--context", "context_name", default=None, help="K8s context name. Uses current context if omitted.")
@click.option("--name", default="cache", show_default=True, help="Helm release name.")
@click.option("--namespace", default=None, help="Kubernetes namespace (defaults to context namespace, then 'sep').")
@click.option("--chart-version", required=True, help="Helm chart version to upgrade to (e.g. 453.0.0).")
@click.option("--repo-username", default=None, help="Starburst Harbor username (or env K4S_STARBURST_REPO_USERNAME).")
@click.option("--repo-password", default=None, help="Starburst Harbor password (or env K4S_STARBURST_REPO_PASSWORD).")
@click.option("--values-file", "values_files", type=click.Path(exists=True, dir_okay=False), multiple=True, help="Helm values YAML file (repeatable).")
@click.option("--set", "set_values", multiple=True, help="Helm --set overrides (repeatable).")
@click.option("--timeout", default="20m", show_default=True, help="Rollout timeout.")
@click.option("--force", is_flag=True, help="Delete pods immediately after upgrade.")
@click.option("--dry-run", is_flag=True, help="Print the plan without applying changes.")
@click.option("-q", "--quiet", is_flag=True, help="Only print final results and errors.")
@click.option("-v", "--verbose", count=True, help="Increase verbosity (-v logs, -vv debug).")
@click.pass_context
def upgrade_cache(
    ctx, context_name, name, namespace, chart_version, repo_username, repo_password,
    values_files, set_values, timeout, force, dry_run, quiet, verbose,
):
    """Upgrade an existing Starburst Cache Service release via Helm (OCI chart)."""
    state: CliState = ctx.obj["state"]
    apply_command_ui_overrides(ctx, state, quiet=quiet, verbose=verbose)
    ui = state.ui

    try:
        c = resolve_k8s_target(state, context_name)
        kubeconfig = kubeconfig_path_for(c)
        ns = namespace or c.namespace or "sep"

        plan = StarburstOciComponentInstallPlan(
            component="cache",
            release_name=name,
            namespace=ns,
            kubeconfig_path=kubeconfig,
            kubectl_context=c.kubectl_context,
            oci_chart_ref="oci://harbor.starburstdata.net/starburstdata/charts/starburst-cache-service",
            chart_version=chart_version,
            repo_username=repo_username,
            repo_password=repo_password,
            values_files=list(values_files) if values_files else None,
            set_values=list(set_values) if set_values else None,
            timeout=timeout,
        )
        plan = starburst_oci_plan_from_env(plan)

        ex = Executor()
        steps = build_starburst_oci_upgrade_steps(ui, ex, plan, force=force)
        if dry_run:
            run_steps(ui, steps, dry_run=True)
        else:
            with ex:
                run_steps(ui, steps, dry_run=False)

        if not dry_run:
            state.history.append(
                action="upgrade",
                product="cache",
                context=c.name,
                params={"namespace": ns, "chart_version": chart_version, "release": name},
            )
            ui.success("Cache service upgraded successfully.")
    except Exception as e:
        exit_with_error(ctx, ui, e, code=1)


# ---------------------------------------------------------------------------
# datafloem
# ---------------------------------------------------------------------------

@upgrade.command("datafloem", short_help="")
@click.option("--context", "context_name", default=None, help="K8s context name. Uses current context if omitted.")
@click.option("--name", default="dfl", show_default=True, help="Helm release name.")
@click.option("--namespace", default=None, help="Kubernetes namespace (defaults to context namespace, then 'datafloem').")
@click.option("--chart-version", required=True, help="Helm chart version to upgrade to.")
@click.option("--repo-username", default=None, help="Datafloem Helm repo username (or env K4S_DATAFLOEM_REPO_USERNAME).")
@click.option("--repo-password", default=None, help="Datafloem Helm repo password (or env K4S_DATAFLOEM_REPO_PASSWORD).")
@click.option("--registry-server", default="docker.repo.datafloem.com", show_default=True, help="Docker registry server for Datafloem images.")
@click.option("--registry-username", default=None, help="Docker registry username (or env K4S_DATAFLOEM_REGISTRY_USERNAME).")
@click.option("--registry-password", default=None, help="Docker registry password (or env K4S_DATAFLOEM_REGISTRY_PASSWORD).")
@click.option("--image-pull-secret-name", default="dflregcred", show_default=True, help="Kubernetes imagePullSecret name to use/create.")
@click.option("--jwt-secret", default=None, help="JWT secret value for backend auth (or env K4S_DATAFLOEM_JWT_SECRET).")
@click.option("--license-file", default=None, type=click.Path(exists=True, dir_okay=False), help="Path to Datafloem license file.")
@click.option("--license-secret-name", default="dfl-license", show_default=True, help="Kubernetes secret name for the license.")
@click.option("--values-file", "values_files", type=click.Path(exists=True, dir_okay=False), multiple=True, help="Helm values YAML file (repeatable).")
@click.option("--set", "set_values", multiple=True, help="Helm --set overrides (repeatable).")
@click.option("--timeout", default="20m", show_default=True, help="Rollout timeout.")
@click.option("--force", is_flag=True, help="Delete pods immediately after upgrade.")
@click.option("--dry-run", is_flag=True, help="Print the plan without applying changes.")
@click.option("-q", "--quiet", is_flag=True, help="Only print final results and errors.")
@click.option("-v", "--verbose", count=True, help="Increase verbosity (-v logs, -vv debug).")
@click.pass_context
def upgrade_datafloem(
    ctx,
    context_name,
    name,
    namespace,
    chart_version,
    repo_username,
    repo_password,
    registry_server,
    registry_username,
    registry_password,
    image_pull_secret_name,
    jwt_secret,
    license_file,
    license_secret_name,
    values_files,
    set_values,
    timeout,
    force,
    dry_run,
    quiet,
    verbose,
):
    """Upgrade an existing Datafloem release via Helm."""
    state: CliState = ctx.obj["state"]
    apply_command_ui_overrides(ctx, state, quiet=quiet, verbose=verbose)
    ui = state.ui

    try:
        c = resolve_k8s_target(state, context_name)
        kubeconfig = kubeconfig_path_for(c)
        ns = namespace or c.namespace or "datafloem"

        plan = DatafloemInstallPlan(
            release_name=name,
            namespace=ns,
            kubeconfig_path=kubeconfig,
            kubectl_context=c.kubectl_context,
            chart_version=chart_version,
            repo_username=repo_username,
            repo_password=repo_password,
            registry_server=registry_server,
            registry_username=registry_username,
            registry_password=registry_password,
            image_pull_secret_name=image_pull_secret_name,
            jwt_secret=jwt_secret,
            license_file=license_file,
            license_secret_name=license_secret_name,
            values_files=list(values_files) if values_files else None,
            set_values=list(set_values) if set_values else None,
            timeout=timeout,
        )
        plan = datafloem_plan_from_env(plan)

        ex = Executor()
        steps = build_datafloem_upgrade_steps(ui, ex, plan, force=force)
        if dry_run:
            run_steps(ui, steps, dry_run=True)
        else:
            with ex:
                run_steps(ui, steps, dry_run=False)

        if not dry_run:
            state.history.append(
                action="upgrade",
                product="datafloem",
                context=c.name,
                params={"namespace": ns, "chart_version": chart_version, "release": name},
            )
            ui.success("Datafloem upgraded successfully.")
    except Exception as e:
        exit_with_error(ctx, ui, e, code=1)
