"use server";

import { revalidatePath } from "next/cache";
import { z } from "zod";

import { signOut } from "@/auth.config";
import { apiBaseUrl, getAuthHeaders } from "@/lib/helper";
import { handleApiError, handleApiResponse } from "@/lib/server-actions-helper";

export const getAllTenants = async () => {
  const headers = await getAuthHeaders({ contentType: false });
  const url = new URL(`${apiBaseUrl}/tenants`);

  try {
    const response = await fetch(url.toString(), {
      method: "GET",
      headers,
    });

    if (!response.ok) {
      throw new Error(`Failed to fetch tenants data: ${response.statusText}`);
    }

    return handleApiResponse(response);
  } catch (error) {
    console.error("Error fetching tenants:", error);
    return undefined;
  }
};

const editTenantFormSchema = z
  .object({
    tenantId: z.string(),
    name: z.string().trim().min(1, { message: "Name is required" }),
    currentName: z.string(),
  })
  .refine((data) => data.name !== data.currentName, {
    message: "Name must be different from the current name",
    path: ["name"],
  });

export type UpdateTenantNameState =
  | { errors: { name?: string } }
  | { success: string }
  | { error: string };

export async function updateTenantName(
  _prevState: UpdateTenantNameState | null,
  formData: FormData,
): Promise<UpdateTenantNameState> {
  const headers = await getAuthHeaders({ contentType: true });
  const formDataObject = Object.fromEntries(formData);
  const validatedData = editTenantFormSchema.safeParse(formDataObject);

  if (!validatedData.success) {
    const formFieldErrors = validatedData.error.flatten().fieldErrors;

    return {
      errors: {
        name: formFieldErrors?.name?.[0],
      },
    };
  }

  const { tenantId, name } = validatedData.data;

  const payload = {
    data: {
      type: "tenants",
      id: tenantId,
      attributes: {
        name: name.trim(),
      },
    },
  };

  try {
    const url = new URL(`${apiBaseUrl}/tenants/${tenantId}`);
    const response = await fetch(url.toString(), {
      method: "PATCH",
      headers,
      body: JSON.stringify(payload),
    });

    if (!response.ok) {
      throw new Error(`Failed to update tenant name: ${response.statusText}`);
    }

    await handleApiResponse(response, "/profile", false);
    return { success: "Tenant name updated successfully!" };
  } catch (error) {
    return handleApiError(error);
  }
}

const switchTenantSchema = z.object({
  tenantId: z.uuid(),
});

interface SwitchTenantSuccess {
  success: true;
  accessToken: string;
  refreshToken: string;
}

interface SwitchTenantError {
  error: string;
}

export type SwitchTenantState = SwitchTenantSuccess | SwitchTenantError;

export async function switchTenant(
  _prevState: SwitchTenantState | null,
  formData: FormData,
): Promise<SwitchTenantState> {
  const formDataObject = Object.fromEntries(formData);
  const validatedData = switchTenantSchema.safeParse(formDataObject);

  if (!validatedData.success) {
    return { error: "Invalid tenant ID" };
  }

  const { tenantId } = validatedData.data;
  const headers = await getAuthHeaders({ contentType: true });

  const payload = {
    data: {
      type: "tokens-switch-tenant",
      attributes: {
        tenant_id: tenantId,
      },
    },
  };

  try {
    const url = new URL(`${apiBaseUrl}/tokens/switch`);
    const response = await fetch(url.toString(), {
      method: "POST",
      headers,
      body: JSON.stringify(payload),
    });

    if (!response.ok) {
      const errorData = await response.json().catch(() => null);
      const errorDetail =
        errorData?.errors?.[0]?.detail ||
        `Failed to switch tenant: ${response.statusText}`;
      throw new Error(errorDetail);
    }

    const data = await response.json();
    const accessToken = data?.data?.attributes?.access;
    const refreshToken = data?.data?.attributes?.refresh;

    if (!accessToken || !refreshToken) {
      throw new Error("Missing tokens in switch tenant response");
    }

    return { success: true, accessToken, refreshToken };
  } catch (error) {
    return handleApiError(error);
  }
}

const createTenantSchema = z.object({
  name: z
    .string()
    .trim()
    .min(1, { message: "Name is required" })
    .max(100, { message: "Name must be 100 characters or less" }),
});

interface CreateTenantSuccess {
  success: true;
  tenantId: string;
}

interface CreateTenantError {
  error: string;
}

export type CreateTenantState = CreateTenantSuccess | CreateTenantError;

export async function createTenant(
  _prevState: CreateTenantState | null,
  formData: FormData,
): Promise<CreateTenantState> {
  const formDataObject = Object.fromEntries(formData);
  const validatedData = createTenantSchema.safeParse(formDataObject);

  if (!validatedData.success) {
    const fieldErrors = validatedData.error.flatten().fieldErrors;
    return { error: fieldErrors?.name?.[0] || "Invalid input" };
  }

  const { name } = validatedData.data;
  const headers = await getAuthHeaders({ contentType: true });

  const payload = {
    data: {
      type: "tenants",
      attributes: { name },
    },
  };

  try {
    const url = new URL(`${apiBaseUrl}/tenants`);
    const response = await fetch(url.toString(), {
      method: "POST",
      headers,
      body: JSON.stringify(payload),
    });

    if (!response.ok) {
      const errorData = await response.json().catch(() => null);
      const errorDetail =
        errorData?.errors?.[0]?.detail ||
        `Failed to create tenant: ${response.statusText}`;
      throw new Error(errorDetail);
    }

    const data = await response.json();
    const tenantId = data?.data?.id;

    if (!tenantId) {
      throw new Error("Missing tenant ID in create response");
    }

    revalidatePath("/profile");
    return { success: true, tenantId };
  } catch (error) {
    return handleApiError(error);
  }
}

const deleteTenantSchema = z.object({
  tenantId: z.uuid(),
});

const switchThenDeleteTenantSchema = z.object({
  tenantId: z.uuid(),
  targetTenantId: z.uuid(),
});

interface DeleteTenantSuccess {
  success: true;
}

interface DeleteTenantError {
  error: string;
}

export type DeleteTenantState = DeleteTenantSuccess | DeleteTenantError;

export async function deleteTenant(
  _prevState: DeleteTenantState | null,
  formData: FormData,
): Promise<DeleteTenantState> {
  const formDataObject = Object.fromEntries(formData);
  const validatedData = deleteTenantSchema.safeParse(formDataObject);

  if (!validatedData.success) {
    return { error: "Invalid tenant ID" };
  }

  const { tenantId } = validatedData.data;
  const headers = await getAuthHeaders({ contentType: false });

  try {
    const url = new URL(`${apiBaseUrl}/tenants/${tenantId}`);
    const response = await fetch(url.toString(), {
      method: "DELETE",
      headers,
    });

    if (!response.ok) {
      const errorData = await response.json().catch(() => null);
      const errorDetail =
        errorData?.errors?.[0]?.detail ||
        `Failed to delete tenant: ${response.statusText}`;
      throw new Error(errorDetail);
    }

    revalidatePath("/profile");
    return { success: true };
  } catch (error) {
    return handleApiError(error);
  }
}

export async function deleteTenantThenSignOut(
  _prevState: DeleteTenantState | null,
  formData: FormData,
): Promise<DeleteTenantState> {
  const formDataObject = Object.fromEntries(formData);
  const validatedData = deleteTenantSchema.safeParse(formDataObject);

  if (!validatedData.success) {
    return { error: "Invalid tenant ID" };
  }

  const { tenantId } = validatedData.data;
  const headers = await getAuthHeaders({ contentType: false });

  try {
    const url = new URL(`${apiBaseUrl}/tenants/${tenantId}`);
    const response = await fetch(url.toString(), {
      method: "DELETE",
      headers,
    });

    if (!response.ok) {
      const errorData = await response.json().catch(() => null);
      const errorDetail =
        errorData?.errors?.[0]?.detail ||
        `Failed to delete tenant: ${response.statusText}`;
      throw new Error(errorDetail);
    }
  } catch (error) {
    return handleApiError(error);
  }

  // Deleting the last tenant also removes the user account server-side, so
  // there is no profile left to revalidate; close the session instead.
  // signOut redirects by throwing, so it must stay outside the try/catch.
  await signOut({ redirectTo: "/sign-in" });

  // Unreachable: signOut always redirects. Present to satisfy the return type.
  return { success: true };
}

interface SwitchThenDeleteSuccess {
  success: true;
  accessToken: string;
  refreshToken: string;
}

interface SwitchThenDeleteError {
  error: string;
  accessToken?: string;
  refreshToken?: string;
}

export type SwitchThenDeleteTenantState =
  | SwitchThenDeleteSuccess
  | SwitchThenDeleteError;

export async function switchThenDeleteTenant(
  _prevState: SwitchThenDeleteTenantState | null,
  formData: FormData,
): Promise<SwitchThenDeleteTenantState> {
  const formDataObject = Object.fromEntries(formData);
  const validatedData = switchThenDeleteTenantSchema.safeParse(formDataObject);

  if (!validatedData.success) {
    return { error: "Invalid tenant or target tenant ID" };
  }

  const { tenantId, targetTenantId } = validatedData.data;
  const headers = await getAuthHeaders({ contentType: true });

  // Step 1: Switch to the target tenant (current token is still valid)
  const switchPayload = {
    data: {
      type: "tokens-switch-tenant",
      attributes: {
        tenant_id: targetTenantId,
      },
    },
  };

  let newAccessToken: string;
  let newRefreshToken: string;

  try {
    const switchUrl = new URL(`${apiBaseUrl}/tokens/switch`);
    const switchResponse = await fetch(switchUrl.toString(), {
      method: "POST",
      headers,
      body: JSON.stringify(switchPayload),
    });

    if (!switchResponse.ok) {
      const errorData = await switchResponse.json().catch(() => null);
      const errorDetail =
        errorData?.errors?.[0]?.detail ||
        `Failed to switch tenant: ${switchResponse.statusText}`;
      throw new Error(errorDetail);
    }

    const switchData = await switchResponse.json();
    newAccessToken = switchData?.data?.attributes?.access;
    newRefreshToken = switchData?.data?.attributes?.refresh;

    if (!newAccessToken || !newRefreshToken) {
      throw new Error("Missing tokens in switch tenant response");
    }
  } catch (error) {
    return handleApiError(error);
  }

  // Step 2: Delete the old tenant using the NEW token
  const deleteHeaders: Record<string, string> = {
    Accept: "application/vnd.api+json",
    Authorization: `Bearer ${newAccessToken}`,
  };

  try {
    const deleteUrl = new URL(`${apiBaseUrl}/tenants/${tenantId}`);
    const deleteResponse = await fetch(deleteUrl.toString(), {
      method: "DELETE",
      headers: deleteHeaders,
    });

    if (!deleteResponse.ok) {
      const errorData = await deleteResponse.json().catch(() => null);
      const errorDetail =
        errorData?.errors?.[0]?.detail ||
        `Failed to delete tenant: ${deleteResponse.statusText}`;
      // Switch succeeded but delete failed — return tokens so client can still update session
      return {
        error: errorDetail,
        accessToken: newAccessToken,
        refreshToken: newRefreshToken,
      };
    }

    revalidatePath("/profile");
    return {
      success: true,
      accessToken: newAccessToken,
      refreshToken: newRefreshToken,
    };
  } catch (error) {
    // Switch succeeded but delete threw — return tokens so client can still update session
    const errorResult = handleApiError(error);
    return {
      ...errorResult,
      accessToken: newAccessToken,
      refreshToken: newRefreshToken,
    };
  }
}
