import { describe, expect, it, afterEach } from "vitest";
import { classifyHostname } from "@/core/tenant/classify-hostname";
import {
  isValidTenantSlug,
  normalizeSlug,
  suggestSlugAlternatives,
} from "@/core/tenant/slug";
import { tenantCacheKey, MemoryCache } from "@/core/cache/cache";
import {
  assertSameTenant,
  authorize,
  permissionsForRole,
} from "@/core/authorization/authorize";
import {
  buildTenantSubdomainUrl,
  getCanonicalTenantSubdomain,
  getTenantPublicHostLabel,
  isLocalHostingEnvironment,
  isReservedSubdomain,
} from "@/core/config/domains";

describe("hostname classification", () => {
  it("classifies platform hosts", () => {
    expect(classifyHostname("localhost:3000").kind).toBe("platform");
    expect(classifyHostname("archi.com").kind).toBe("platform");
    expect(classifyHostname("www.archi.com").kind).toBe("platform");
  });

  it("classifies app/admin/api hosts", () => {
    expect(classifyHostname("app.archi.com").kind).toBe("app");
    expect(classifyHostname("admin.archi.com").kind).toBe("admin");
    expect(classifyHostname("api.archi.com").kind).toBe("api");
  });

  it("classifies tenant subdomains", () => {
    expect(classifyHostname("mukhsin.archi.com")).toEqual({
      kind: "tenant_subdomain",
      slug: "mukhsin",
    });
    expect(classifyHostname("mukhsin.localhost")).toEqual({
      kind: "tenant_subdomain",
      slug: "mukhsin",
    });
    expect(classifyHostname("mukhsin.localhost:3001")).toEqual({
      kind: "tenant_subdomain",
      slug: "mukhsin",
    });
  });

  it("does not treat reserved subdomains as tenants", () => {
    expect(classifyHostname("www.archi.com").kind).toBe("platform");
    expect(classifyHostname("login.archi.com").kind).toBe("unknown");
  });

  it("classifies custom domains", () => {
    expect(classifyHostname("mukhsin.com")).toEqual({
      kind: "custom_domain",
      domain: "mukhsin.com",
    });
  });
});

describe("slug rules", () => {
  it("normalizes studio names", () => {
    expect(normalizeSlug("Mukhsin Architecture")).toBe("mukhsin-architecture");
  });

  it("rejects reserved slugs", () => {
    expect(isReservedSubdomain("admin")).toBe(true);
    expect(isValidTenantSlug("admin")).toBe(false);
    expect(isValidTenantSlug("mukhsin")).toBe(true);
  });

  it("suggests alternatives without overwriting", () => {
    const suggestions = suggestSlugAlternatives("mukhsin", "Mukhsin Architecture");
    expect(suggestions.length).toBeGreaterThan(0);
    expect(suggestions.every(isValidTenantSlug)).toBe(true);
  });
});

describe("cache isolation keys", () => {
  it("includes tenant identity", () => {
    expect(tenantCacheKey("tenant-a", "projects", "published")).toBe(
      "tenant:tenant-a:projects:published",
    );
    expect(tenantCacheKey("tenant-b", "projects", "published")).toBe(
      "tenant:tenant-b:projects:published",
    );
  });

  it("never serves tenant A cache to tenant B", () => {
    const cache = new MemoryCache();
    cache.set(tenantCacheKey("a", "projects"), ["project-a"], 60);
    expect(cache.get(tenantCacheKey("b", "projects"))).toBeUndefined();
    expect(cache.get(tenantCacheKey("a", "projects"))).toEqual(["project-a"]);
  });
});

describe("authorization isolation", () => {
  it("denies cross-tenant resource access", () => {
    expect(assertSameTenant("tenant-a", "tenant-b")).toEqual({
      allowed: false,
      reason: "Cross-tenant access denied",
    });
  });

  it("requires membership and permission", () => {
    const principal = {
      userId: "user-1",
      platformRole: "none" as const,
      tenantMemberships: [
        {
          tenantId: "tenant-a",
          role: "contributor" as const,
          permissions: [],
          status: "active",
        },
      ],
    };

    expect(
      authorize(principal, "projects:publish", { tenantId: "tenant-a" }).allowed,
    ).toBe(false);

    expect(
      authorize(principal, "projects:view", { tenantId: "tenant-b" }).allowed,
    ).toBe(false);

    expect(permissionsForRole("owner")).toContain("billing:manage");
  });
});

describe("environment-aware tenant URLs", () => {
  const previous = {
    APP_URL: process.env.APP_URL,
    NEXT_PUBLIC_APP_URL: process.env.NEXT_PUBLIC_APP_URL,
    AUTH_URL: process.env.AUTH_URL,
  };

  afterEach(() => {
    process.env.APP_URL = previous.APP_URL;
    process.env.NEXT_PUBLIC_APP_URL = previous.NEXT_PUBLIC_APP_URL;
    process.env.AUTH_URL = previous.AUTH_URL;
  });

  it("uses *.localhost with APP_URL port while hosting locally", () => {
    process.env.NEXT_PUBLIC_APP_URL = "http://localhost:3001";
    process.env.APP_URL = "http://localhost:3001";

    expect(isLocalHostingEnvironment()).toBe(true);
    expect(getTenantPublicHostLabel("mukhsin")).toBe("mukhsin.localhost:3001");
    expect(buildTenantSubdomainUrl("mukhsin")).toBe(
      "http://mukhsin.localhost:3001/",
    );
    expect(getCanonicalTenantSubdomain("mukhsin")).toBe("mukhsin.archi.com");
  });

  it("uses /studio/{slug} path preview when request host is a LAN IP", () => {
    process.env.NEXT_PUBLIC_APP_URL = "http://localhost:3000";
    process.env.APP_URL = "http://localhost:3000";

    expect(
      buildTenantSubdomainUrl("mukhsin", {
        requestHost: "192.168.18.2:3000",
      }),
    ).toBe("http://192.168.18.2:3000/studio/mukhsin");
    expect(
      getTenantPublicHostLabel("mukhsin", {
        requestHost: "192.168.18.2:3000",
      }),
    ).toBe("192.168.18.2:3000/studio/mukhsin");
    expect(
      buildTenantSubdomainUrl("mukhsin", {
        requestHost: "192.168.18.7:3000",
        path: "/about",
      }),
    ).toBe("http://192.168.18.7:3000/studio/mukhsin/about");
  });

  it("prefers LAN path URLs when request host is an IP even if APP_URL is localhost", () => {
    process.env.NEXT_PUBLIC_APP_URL = "http://localhost:3000";
    process.env.APP_URL = "http://localhost:3000";

    expect(
      buildTenantSubdomainUrl("mukhsin", {
        requestHost: "192.168.18.7:3000",
      }),
    ).toBe("http://192.168.18.7:3000/studio/mukhsin");
    expect(
      getTenantPublicHostLabel("mukhsin", {
        requestHost: "192.168.18.7:3000",
      }),
    ).toBe("192.168.18.7:3000/studio/mukhsin");
  });

  it("uses /studio/{slug} when APP_URL itself is a LAN IP", () => {
    process.env.NEXT_PUBLIC_APP_URL = "http://192.168.18.7:3000";
    process.env.APP_URL = "http://192.168.18.7:3000";

    expect(isLocalHostingEnvironment()).toBe(true);
    expect(getTenantPublicHostLabel("mukhsin")).toBe(
      "192.168.18.7:3000/studio/mukhsin",
    );
    expect(buildTenantSubdomainUrl("mukhsin")).toBe(
      "http://192.168.18.7:3000/studio/mukhsin",
    );
  });

  it("uses production subdomain online", () => {
    process.env.NEXT_PUBLIC_APP_URL = "https://archi.com";
    process.env.APP_URL = "https://archi.com";

    expect(isLocalHostingEnvironment()).toBe(false);
    expect(getTenantPublicHostLabel("mukhsin")).toBe("mukhsin.archi.com");
    expect(buildTenantSubdomainUrl("mukhsin")).toBe(
      "https://mukhsin.archi.com/",
    );
  });
});
