"use server";

import { redirect } from "next/navigation";

import { getLatestFindings } from "@/actions/findings";
import { listOrganizationsSafe } from "@/actions/organizations/organizations";
import {
  apiBaseUrl,
  FINDINGS_FILTERED_SORT,
  GENERIC_SERVER_ERROR_MESSAGE,
  getAuthHeaders,
  sanitizeErrorMessage,
} from "@/lib";
import { appendSanitizedProviderTypeFilters } from "@/lib/provider-filters";
import { handleApiResponse } from "@/lib/server-actions-helper";
import { OrganizationResource } from "@/types/organizations";

export const getResources = async ({
  page = 1,
  query = "",
  sort = "",
  filters = {},
  pageSize = 10,
  include = "",
  fields = [],
}: {
  page?: number;
  query?: string;
  sort?: string;
  filters?: Record<string, string | string[] | undefined>;
  pageSize?: number;
  include?: string;
  fields?: string[];
}) => {
  const headers = await getAuthHeaders({ contentType: false });

  if (isNaN(Number(page)) || page < 1) redirect("resources");

  const url = new URL(`${apiBaseUrl}/resources`);

  if (fields.length > 0) {
    url.searchParams.append("fields[resources]", fields.join(","));
  }

  if (page) url.searchParams.append("page[number]", page.toString());
  if (pageSize) url.searchParams.append("page[size]", pageSize.toString());
  if (include) url.searchParams.append("include", include);
  if (query) url.searchParams.append("filter[search]", query);
  if (sort) url.searchParams.append("sort", sort);

  appendSanitizedProviderTypeFilters(url, filters);

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

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

export const getLatestResources = async ({
  page = 1,
  query = "",
  sort = "",
  include = "",
  filters = {},
  pageSize = 10,
  fields = [],
}: {
  page?: number;
  query?: string;
  sort?: string;
  filters?: Record<string, string | string[] | undefined>;
  pageSize?: number;
  include?: string;
  fields?: string[];
}) => {
  const headers = await getAuthHeaders({ contentType: false });

  if (isNaN(Number(page)) || page < 1) redirect("resources");

  const url = new URL(`${apiBaseUrl}/resources/latest`);

  if (fields.length > 0) {
    url.searchParams.append("fields[resources]", fields.join(","));
  }

  if (page) url.searchParams.append("page[number]", page.toString());
  if (pageSize) url.searchParams.append("page[size]", pageSize.toString());
  if (include) url.searchParams.append("include", include);
  if (query) url.searchParams.append("filter[search]", query);
  if (sort) url.searchParams.append("sort", sort);

  appendSanitizedProviderTypeFilters(url, filters);

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

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

export const getMetadataInfo = async ({
  query = "",
  sort = "",
  filters = {},
}: {
  query?: string;
  sort?: string;
  filters?: Record<string, string | string[] | undefined>;
}) => {
  const headers = await getAuthHeaders({ contentType: false });

  const url = new URL(`${apiBaseUrl}/resources/metadata`);

  if (query) url.searchParams.append("filter[search]", query);
  if (sort) url.searchParams.append("sort", sort);

  appendSanitizedProviderTypeFilters(url, filters);

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

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

export const getLatestMetadataInfo = async ({
  query = "",
  sort = "",
  filters = {},
}: {
  query?: string;
  sort?: string;
  filters?: Record<string, string | string[] | undefined>;
}) => {
  const headers = await getAuthHeaders({ contentType: false });

  const url = new URL(`${apiBaseUrl}/resources/metadata/latest`);

  if (query) url.searchParams.append("filter[search]", query);
  if (sort) url.searchParams.append("sort", sort);

  appendSanitizedProviderTypeFilters(url, filters);

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

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

const SAFE_RESOURCE_ID_PATTERN = /^[a-zA-Z0-9_-]+$/;

export const getResourceEvents = async (
  resourceId: string,
  {
    includeReadEvents = false,
    lookbackDays = 90,
    pageSize = 50,
  }: {
    includeReadEvents?: boolean;
    lookbackDays?: number;
    pageSize?: number;
  } = {},
) => {
  if (!SAFE_RESOURCE_ID_PATTERN.test(resourceId)) {
    return { error: "Invalid resource ID format." };
  }

  const headers = await getAuthHeaders({ contentType: false });

  const url = new URL(`${apiBaseUrl}/resources/${resourceId}/events`);
  url.searchParams.append("include_read_events", String(includeReadEvents));
  url.searchParams.append("lookback_days", String(lookbackDays));
  url.searchParams.append("page[size]", String(pageSize));

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

    if (!response.ok) {
      const rawText = await response.text().catch(() => "");
      const contentType =
        response.headers.get("content-type")?.toLowerCase() || "";
      const defaultError = "An error occurred while fetching events.";
      const fallbackError = contentType.includes("text/html")
        ? GENERIC_SERVER_ERROR_MESSAGE
        : response.statusText || defaultError;
      try {
        const errorData = rawText ? JSON.parse(rawText) : null;
        const errorMessage =
          errorData?.errors?.[0]?.detail ||
          errorData?.error ||
          errorData?.message ||
          rawText ||
          fallbackError;
        return {
          error: sanitizeErrorMessage(String(errorMessage), fallbackError),
          status: response.status,
        };
      } catch {
        return {
          error: sanitizeErrorMessage(rawText || fallbackError, fallbackError),
          status: response.status,
        };
      }
    }

    return handleApiResponse(response);
  } catch (error) {
    console.error("Error fetching resource events:", error);
    return { error: "An unexpected error occurred." };
  }
};

export const getResourceById = async (
  id: string,
  {
    fields = [],
    include = [],
  }: {
    fields?: string[];
    include?: string[];
  } = {},
) => {
  const headers = await getAuthHeaders({ contentType: false });

  const url = new URL(`${apiBaseUrl}/resources/${id}`);

  if (fields.length > 0) {
    url.searchParams.append("fields[resources]", fields.join(","));
  }

  if (include.length > 0) {
    url.searchParams.append("include", include.join(","));
  }

  try {
    const resource = await fetch(url.toString(), {
      headers,
    });

    if (!resource.ok) {
      throw new Error(`Error fetching resource: ${resource.status}`);
    }

    return handleApiResponse(resource);
  } catch (error) {
    console.error("Error fetching resource by ID:", error);
    return undefined;
  }
};

export const getResourceDrawerData = async ({
  resourceId,
  resourceUid,
  providerId,
  providerType,
  page = 1,
  pageSize = 10,
  query = "",
}: {
  resourceId: string;
  resourceUid: string;
  providerId: string;
  providerType: string;
  page?: number;
  pageSize?: number;
  query?: string;
}) => {
  const isCloudEnv = process.env.NEXT_PUBLIC_IS_CLOUD_ENV === "true";

  const [resourceData, findingsResponse, organizationsResponse] =
    await Promise.all([
      getResourceById(resourceId, { fields: ["tags"] }),
      getLatestFindings({
        page,
        pageSize,
        query,
        sort: FINDINGS_FILTERED_SORT,
        filters: {
          "filter[resource_uid]": resourceUid,
          "filter[status]": "FAIL",
        },
      }),
      isCloudEnv && providerType === "aws"
        ? listOrganizationsSafe()
        : Promise.resolve({ data: [] }),
    ]);

  const providerOrg =
    providerType === "aws"
      ? (organizationsResponse.data.find((organization: OrganizationResource) =>
          organization.relationships?.providers?.data?.some(
            (provider: { id: string }) => provider.id === providerId,
          ),
        ) ?? null)
      : null;

  return {
    findings: findingsResponse?.data ?? [],
    findingsMeta: findingsResponse?.meta ?? null,
    providerOrg,
    resourceTags: resourceData?.data?.attributes.tags ?? {},
  };
};
