import { getSessionUser, canMutate } from "@/lib/session";
import { getTranslations, getFormatter } from "next-intl/server";
import { redirect, notFound } from "next/navigation";
import { getTenant } from "@/lib/k8s";
import { canUserSeeTenant } from "@/lib/visibility";
import {
  getPendingResumeRequestForTenant,
  listSkillActivationRequestsForTenant,
  listSkillPricing,
} from "@/lib/db";
import { StatusBadge } from "@/components/ui/status-badge";
import { WarningBadge } from "@/components/ui/warning-badge";
import { UsageDisplay } from "@/components/dashboard/usage-display";
import { PackageList } from "@/components/packages/package-list";
import { WorkspaceEditor } from "@/components/packages/workspace-editor";
import { ChannelUsers } from "@/components/channel-users/channel-users";
import { AssignedUsersPanel } from "@/components/tenants/assigned-users-panel";
import { SubscriptionToggle } from "@/components/tenants/subscription-toggle";
import { ConnectPanel } from "@/components/tenants/connect-panel";
import { formatDateTime, formatRelative } from "@/lib/format";
import { CHANNEL_PACKAGE_IDS } from "@/lib/packages";

// CHANNEL_PACKAGES used to be a hardcoded literal here
// (`["telegram", "discord", "email"]`). It now derives from the
// portal-side catalog so adding a new channel anywhere only requires
// editing src/lib/packages.ts. The `email` channel was dropped as
// part of the Phase A package-model rework — IMAP/SMTP is now the
// `mail` skill instead.
const CHANNEL_PACKAGES = CHANNEL_PACKAGE_IDS;

export default async function TenantDetailPage({
  params,
}: {
  params: Promise<{ name: string; locale: string }>;
}) {
  const user = await getSessionUser();
  if (!user) redirect("/login");

  const { name } = await params;
  const t = await getTranslations("tenantDetail");
  const f = await getFormatter();

  const tenant = await getTenant(name);
  if (!tenant) notFound();

  // Slice 6: visibility check encompasses org membership AND, for
  // user-role members, the tenant_user_assignments check. notFound()
  // (404) rather than redirect/403 to avoid leaking tenant existence.
  if (!(await canUserSeeTenant(user, tenant))) {
    notFound();
  }

  // Slice 5: editable surface gated on owner role. Platform users always
  // can edit; customer-side, only `owner` may. `user`-role members see
  // the same page but with edit controls hidden / fields read-only.
  const canEdit = canMutate(user);

  // Bug 31: customer-side cancel/resume control. Same gate as canEdit
  // — only owners (or platform staff) may toggle the subscription.
  // The current state comes from spec.suspend on the CR.
  const isSuspended = Boolean(tenant.spec.suspend);

  // Bug 37a: when the tenant is suspended, an owner can request
  // reactivation (admin-gated). Look up whether one is in flight so
  // the SubscriptionToggle can render the right state.
  const pendingResumeRequest = isSuspended
    ? await getPendingResumeRequestForTenant(name)
    : null;

  // Bug 7: assigned-users panel is meaningless for personal tenants
  // (sole-owner by definition; the only "assignee" is the owner
  // themselves). We hide the panel when EITHER the CR carries the
  // `pieced.ch/personal=true` label (set at approve time for new
  // personal tenants) OR the viewer is on a personal account (covers
  // legacy tenants approved before the label was added; the customer
  // sees their own personal tenant). Platform admins viewing a legacy
  // unlabeled personal tenant are the only case where this falls
  // through to "show panel" — operators can `kubectl label` to fix.
  const isPersonalTenant =
    tenant.metadata.labels?.["pieced.ch/personal"] === "true" ||
    user.isPersonal;

  const enabledPackages = tenant.spec.packages || [];
  const workspaceFiles = tenant.spec.workspaceFiles || {};
  const enabledChannels = enabledPackages.filter((pkg) =>
    CHANNEL_PACKAGES.includes(pkg)
  );
  const channelUsers = tenant.spec.channelUsers || {};

  // Phase 2.5: surface pending and most-recently-rejected skill
  // activation requests so PackageCard can render the inline
  // "Manual review pending" / "Activation rejected" states.
  // Pricing drives the cost-disclosure dialog before enable.
  // Both fetches are best-effort — an empty list is the safe
  // fallback if the DB call fails (cards just show normal toggles).
  const [activationRequests, skillPricing] = await Promise.all([
    listSkillActivationRequestsForTenant(name).catch(() => []),
    listSkillPricing().catch(() => []),
  ]);

  // Bug 19 fix: every viewer (customer or admin) passes the tenant
  // name to UsageDisplay. The /api/usage route resolves team+alias
  // from the tenant CR's status and applies the visibility check, so
  // no per-role branching is needed here. Previous version only
  // passed identifiers for platform admins; customers got "the first
  // visible tenant" by API fallback, mingling siblings.

  return (
    <div>
      {/* Header */}
      <div className="mb-8 animate-in">
        <div className="flex items-center gap-4">
          <h1 className="font-display text-2xl font-semibold accent-rule">
            {tenant.spec.displayName || name}
          </h1>
          <StatusBadge phase={tenant.status?.phase ?? "Pending"} />
          <WarningBadge warnings={tenant.status?.warnings ?? []} />
        </div>
        {tenant.spec.agentName && (
          <p className="text-sm text-text-secondary mt-3">
            {t("agent")}: {tenant.spec.agentName}
          </p>
        )}
        {tenant.metadata.creationTimestamp && (
          <p
            className="text-xs text-text-muted mt-1"
            title={formatDateTime(tenant.metadata.creationTimestamp, f)}
          >
            {t("provisioned")}{" "}
            {formatRelative(tenant.metadata.creationTimestamp, f)}{" "}
            <span className="text-text-muted/60">
              ({formatDateTime(tenant.metadata.creationTimestamp, f)})
            </span>
          </p>
        )}
      </div>

      {/* Bug 31: prominent banner when the subscription is cancelled.
          Sits between header and content so it's the first thing the
          owner sees. Says clearly what state means, and that data is
          preserved. The Resume action lives in the SubscriptionToggle
          at the bottom — duplicating it here would clutter the banner
          for the much-more-common active case. */}
      {isSuspended && (
        <div className="mb-8 animate-in animate-in-delay-1 bg-amber-500/10 border border-amber-500/30 rounded-xl p-4">
          <div className="flex items-start gap-3">
            <svg
              className="h-5 w-5 text-amber-400 shrink-0 mt-0.5"
              fill="none"
              viewBox="0 0 24 24"
              stroke="currentColor"
              strokeWidth={1.5}
              aria-hidden="true"
            >
              <path
                strokeLinecap="round"
                strokeLinejoin="round"
                d="M12 9v3.75m9-.75a9 9 0 11-18 0 9 9 0 0118 0zM12 15.75h.008v.008H12v-.008z"
              />
            </svg>
            <div className="min-w-0">
              <div className="text-sm font-semibold text-amber-300">
                {t("suspendedTitle")}
              </div>
              <div className="text-xs text-text-secondary mt-1">
                {t("suspendedDescription")}
              </div>
              {/* Retention countdown. suspendedAt is stamped by the
                  operator on first transition to suspended; missing
                  values fall through silently rather than rendering
                  garbage (operator hasn't reconciled yet, edge case).
                  The 60-day window is the operator's
                  retentionAfterSuspend constant; if you change one,
                  change both. We don't expose the constant via API —
                  the value rarely changes and duplicating it here
                  beats fetching a single int over the network. */}
              {tenant.status?.suspendedAt && (() => {
                const suspendedAt = new Date(tenant.status.suspendedAt);
                const deletionAt = new Date(suspendedAt);
                deletionAt.setDate(deletionAt.getDate() + 60);
                const now = new Date();
                const msRemaining = deletionAt.getTime() - now.getTime();
                const daysRemaining = Math.max(
                  0,
                  Math.ceil(msRemaining / (1000 * 60 * 60 * 24))
                );
                // < 7 days: red/critical to draw attention. Otherwise
                // amber, matching the banner.
                const urgent = daysRemaining < 7;
                return (
                  <div
                    className={`text-xs mt-2 ${
                      urgent ? "text-red-400" : "text-text-muted"
                    }`}
                  >
                    {t("suspendedSince", {
                      date: formatDateTime(
                        tenant.status.suspendedAt,
                        f
                      ),
                    })}
                    {" · "}
                    {daysRemaining > 0
                      ? t("suspendedDeletionIn", {
                          days: daysRemaining,
                          date: formatDateTime(
                            deletionAt.toISOString(),
                            f
                          ),
                        })
                      : t("suspendedDeletionImminent")}
                  </div>
                );
              })()}
            </div>
          </div>
        </div>
      )}

      {/* Connect: how the customer actually reaches their assistant.
          The portal manages the assistant; the assistant lives in the
          customer's messaging app. This bridges that gap right at the
          top of the page (and calls out the case where no channel is
          enabled, which would otherwise leave a running assistant
          unreachable). */}
      <section className="mb-8 animate-in animate-in-delay-1">
        <ConnectPanel
          enabledChannels={enabledChannels}
          phase={tenant.status?.phase ?? "Pending"}
        />
      </section>

      {/* Usage */}
      <section className="mb-8 animate-in animate-in-delay-1">
        <h2 className="text-xs font-semibold uppercase tracking-wider text-text-muted mb-3">
          {t("usage")}
        </h2>
        <UsageDisplay tenant={name} canEditBudget={canEdit} />
      </section>

      {/* Packages */}
      <section className="mb-8 animate-in animate-in-delay-2">
        <h2 className="text-xs font-semibold uppercase tracking-wider text-text-muted mb-3">
          {t("packages")}
        </h2>
        <PackageList
          tenantName={name}
          enabledPackages={enabledPackages}
          conditions={tenant.status?.conditions}
          canEdit={canEdit}
          activationRequests={activationRequests}
          skillPricing={skillPricing}
        />
      </section>

      {/* Channel Users (authorized users per channel) */}
      {enabledChannels.length > 0 && (
        <section className="mb-8 animate-in animate-in-delay-3">
          <ChannelUsers
            tenantName={name}
            enabledChannels={enabledChannels}
            initialChannelUsers={channelUsers}
            canEdit={canEdit}
          />
        </section>
      )}

      {/* Workspace files */}
      <section className="animate-in animate-in-delay-4">
        <h2 className="text-xs font-semibold uppercase tracking-wider text-text-muted mb-3">
          {t("workspaceFiles")}
        </h2>
        <WorkspaceEditor tenantName={name} files={workspaceFiles} canEdit={canEdit} />
      </section>

      {/* Slice 7: Assigned users — visible to anyone who can see the
          tenant, editable only by owners/platform users. The component
          fetches its own data so the page doesn't need to await.
          Bug 7: hidden entirely for personal tenants. */}
      {!isPersonalTenant && (
        <section className="mt-8 animate-in animate-in-delay-4">
          <h2 className="text-xs font-semibold uppercase tracking-wider text-text-muted mb-3">
            {t("assignedUsers")}
          </h2>
          <AssignedUsersPanel tenantName={name} canEdit={canEdit} />
        </section>
      )}

      {/* Bug 31: subscription cancel/resume — owners + platform staff
          only. Lives at the bottom of the page (rather than near the
          status badge) to add deliberate friction; mis-clicking
          "Cancel subscription" from the top would be too easy. The
          control itself opens a confirmation modal before sending. */}
      {canEdit && (
        <section className="mt-12 pt-8 border-t border-border animate-in animate-in-delay-4">
          <h2 className="text-xs font-semibold uppercase tracking-wider text-text-muted mb-3">
            {t("subscriptionTitle")}
          </h2>
          <p className="text-sm text-text-secondary mb-4">
            {isSuspended
              ? t("subscriptionDescriptionSuspended")
              : t("subscriptionDescriptionActive")}
          </p>
          <SubscriptionToggle
            tenantName={name}
            suspended={isSuspended}
            isPlatform={user.isPlatform}
            pendingResumeRequest={
              pendingResumeRequest
                ? {
                    id: pendingResumeRequest.id,
                    createdAt: pendingResumeRequest.createdAt,
                    customerNotes:
                      pendingResumeRequest.customerNotes ?? null,
                  }
                : null
            }
          />
        </section>
      )}
    </div>
  );
}
