import { createAdminClient } from "@/lib/supabase/admin";
import type { AuthMethod, Organization, OrganizationAuthPolicy } from "@/types";

export interface AuthPolicyMatch {
  organization: Pick<Organization, "id" | "name" | "slug">;
  policy: OrganizationAuthPolicy;
}

export interface AuthPolicyPreview {
  email: string;
  domain: string | null;
  matchedOrganizations: Array<{
    id: string;
    name: string;
    slug: string;
    invite_only: boolean;
    enforce_sso: boolean;
  }>;
  allowedMethods: AuthMethod[];
  autoJoinEnabled: boolean;
}

export const AUTH_METHOD_OPTIONS: Array<{ value: AuthMethod; label: string }> = [
  { value: "password", label: "Password" },
  { value: "magic_link", label: "Magic link" },
  { value: "google", label: "Google" },
  { value: "github", label: "GitHub" },
  { value: "azure", label: "Microsoft" },
];

const ALL_AUTH_METHODS = AUTH_METHOD_OPTIONS.map((option) => option.value);

export function normalizeDomain(input: string) {
  return input.trim().toLowerCase().replace(/^@/, "");
}

export function parseDomainList(input: string) {
  return Array.from(
    new Set(
      input
        .split(",")
        .map(normalizeDomain)
        .filter(Boolean)
    )
  );
}

export function getEmailDomain(email: string) {
  const normalized = email.trim().toLowerCase();
  const atIndex = normalized.lastIndexOf("@");
  if (atIndex < 0) return null;
  const domain = normalized.slice(atIndex + 1);
  return domain || null;
}

function intersectAllowedMethods(policies: OrganizationAuthPolicy[]) {
  return ALL_AUTH_METHODS.filter((method) =>
    policies.every((policy) => policy.allowed_auth_methods.includes(method))
  );
}

export function isAuthMethodAllowed(preview: AuthPolicyPreview | null, method: AuthMethod) {
  if (!preview) return true;
  return preview.allowedMethods.includes(method);
}

export function formatAuthMethodList(methods: AuthMethod[]) {
  return methods
    .map((method) => AUTH_METHOD_OPTIONS.find((option) => option.value === method)?.label || method)
    .join(", ");
}

export async function getAuthPolicyMatchesForEmail(email: string) {
  const domain = getEmailDomain(email);
  if (!domain) {
    return {
      domain: null,
      matches: [] as AuthPolicyMatch[],
    };
  }

  const admin = createAdminClient();
  const { data, error } = await admin
    .from("organization_auth_policies")
    .select(
      "organization_id, allowed_auth_methods, allowed_email_domains, auto_join_domains, invite_only, require_domain_match, enforce_sso, created_at, updated_at, organizations!inner(id, name, slug)"
    );

  if (error || !data) {
    return {
      domain,
      matches: [] as AuthPolicyMatch[],
    };
  }

  const matches = data
    .map((row) => ({
      organization: Array.isArray(row.organizations)
        ? row.organizations[0]
        : row.organizations,
      policy: {
        organization_id: row.organization_id,
        allowed_auth_methods: (row.allowed_auth_methods || []) as AuthMethod[],
        allowed_email_domains: (row.allowed_email_domains || []) as string[],
        auto_join_domains: (row.auto_join_domains || []) as string[],
        invite_only: Boolean(row.invite_only),
        require_domain_match: Boolean(row.require_domain_match),
        enforce_sso: Boolean(row.enforce_sso),
        created_at: row.created_at as string,
        updated_at: row.updated_at as string,
      },
    }))
    .filter((entry): entry is AuthPolicyMatch => Boolean(entry.organization))
    .filter((entry) => {
      const allowedDomains = entry.policy.allowed_email_domains.map(normalizeDomain);
      const autoJoinDomains = entry.policy.auto_join_domains.map(normalizeDomain);
      const matchesAllowed = allowedDomains.includes(domain);
      const matchesAutoJoin = autoJoinDomains.includes(domain);
      return matchesAllowed || matchesAutoJoin;
    });

  return { domain, matches };
}

export async function buildAuthPolicyPreview(email: string): Promise<AuthPolicyPreview> {
  const normalizedEmail = email.trim().toLowerCase();
  const { domain, matches } = await getAuthPolicyMatchesForEmail(normalizedEmail);

  return {
    email: normalizedEmail,
    domain,
    matchedOrganizations: matches.map((entry) => ({
      id: entry.organization.id,
      name: entry.organization.name,
      slug: entry.organization.slug,
      invite_only: entry.policy.invite_only,
      enforce_sso: entry.policy.enforce_sso,
    })),
    allowedMethods: matches.length > 0 ? intersectAllowedMethods(matches.map((entry) => entry.policy)) : ALL_AUTH_METHODS,
    autoJoinEnabled: matches.some((entry) =>
      entry.policy.auto_join_domains.map(normalizeDomain).includes(domain || "")
    ),
  };
}

export async function syncOrganizationAccessForUser(input: {
  userId: string;
  email: string | null | undefined;
}) {
  if (!input.email) {
    return { joinedOrganizationIds: [] as string[] };
  }

  const { domain, matches } = await getAuthPolicyMatchesForEmail(input.email);
  if (!domain || matches.length === 0) {
    return { joinedOrganizationIds: [] as string[] };
  }

  const admin = createAdminClient();
  const joinedOrganizationIds: string[] = [];

  for (const match of matches) {
    const autoJoinDomains = match.policy.auto_join_domains.map(normalizeDomain);
    if (!autoJoinDomains.includes(domain)) {
      continue;
    }

    const { data: existingMembership } = await admin
      .from("organization_members")
      .select("organization_id")
      .eq("organization_id", match.organization.id)
      .eq("user_id", input.userId)
      .maybeSingle();

    if (existingMembership) {
      continue;
    }

    const { error } = await admin.from("organization_members").insert({
      organization_id: match.organization.id,
      user_id: input.userId,
      role: "member",
      title: "Domain member",
      seat_status: "active",
      counts_toward_seat: true,
    });

    if (!error) {
      joinedOrganizationIds.push(match.organization.id);
    }
  }

  return { joinedOrganizationIds };
}

export async function canManageOrganizationWorkspace(input: {
  organizationId: string;
  userId: string;
}) {
  const admin = createAdminClient();
  const [{ data: ownedOrganization }, { data: membership }] = await Promise.all([
    admin
      .from("organizations")
      .select("id")
      .eq("id", input.organizationId)
      .eq("owner_id", input.userId)
      .maybeSingle(),
    admin
      .from("organization_members")
      .select("organization_id, role")
      .eq("organization_id", input.organizationId)
      .eq("user_id", input.userId)
      .maybeSingle(),
  ]);

  if (ownedOrganization) return true;
  return membership?.role === "owner" || membership?.role === "admin";
}
