"use client";

import CopyToClipboard from "@app/components/CopyToClipboard";
import { Button } from "@app/components/ui/button";
import { InfoPopup } from "@app/components/ui/info-popup";
import { SettingsContainer } from "@app/components/Settings";
import { useEnvContext } from "@app/hooks/useEnvContext";
import { createApiClient } from "@app/lib/api";
import { formatSiteResourceDestinationDisplay } from "@app/lib/formatSiteResourceAccess";
import type { ListAllSiteResourcesByOrgResponse } from "@server/routers/siteResource";
import type { ListResourcesResponse } from "@server/routers/resource";
import type ResponseT from "@server/types/Response";
import { useQuery } from "@tanstack/react-query";
import { isAxiosError } from "axios";
import { useTranslations } from "next-intl";
import Link from "next/link";
import { useParams } from "next/navigation";
import { toUnicode } from "punycode";
import { useMemo, useState, type ReactNode } from "react";

const INITIAL_PAGE_SIZE = 5;
const LOAD_MORE_INCREMENT = 20;

type SiteResourceRow =
    ListAllSiteResourcesByOrgResponse["siteResources"][number];

type PublicResourceRow = ListResourcesResponse["resources"][number];

function isForbidden(e: unknown): boolean {
    return isAxiosError(e) && e.response?.status === 403;
}

function isSafeUrlForLink(href: string): boolean {
    try {
        void new URL(href);
        return true;
    } catch {
        return false;
    }
}

/** Meta text inside the left column (width comes from the column wrapper). */
const OVERVIEW_META_CLASS = "w-full min-w-0 text-muted-foreground text-sm";

function publicProtocolLabel(r: PublicResourceRow): string {
    if (r.http) {
        return r.ssl ? "HTTPS" : "HTTP";
    }
    const p = (r.protocol || "").toLowerCase();
    if (p === "tcp") return "TCP";
    if (p === "udp") return "UDP";
    return (r.protocol || "—").toUpperCase();
}

function PublicResourceMeta({ resource: r }: { resource: PublicResourceRow }) {
    return (
        <div className={OVERVIEW_META_CLASS}>
            <div className="truncate font-medium text-foreground">
                {publicProtocolLabel(r)}
            </div>
        </div>
    );
}

function PrivateResourceMeta({ row }: { row: SiteResourceRow }) {
    const t = useTranslations();
    const modeLabel: Record<SiteResourceRow["mode"], string> = {
        host: t("editInternalResourceDialogModeHost"),
        cidr: t("editInternalResourceDialogModeCidr"),
        http: t("editInternalResourceDialogModeHttp")
    };
    const dest = formatSiteResourceDestinationDisplay({
        mode: row.mode,
        destination: row.destination,
        httpHttpsPort: row.destinationPort ?? null,
        scheme: row.scheme
    });
    return (
        <div
            className={OVERVIEW_META_CLASS}
            title={`${modeLabel[row.mode]}\n${dest}`}
        >
            <div className="truncate font-medium text-foreground">
                {modeLabel[row.mode]}
            </div>
        </div>
    );
}

function PublicAccessMethod({ resource: r }: { resource: PublicResourceRow }) {
    const t = useTranslations();
    if (!r.http) {
        return (
            <CopyToClipboard
                text={r.proxyPort?.toString() ?? ""}
                isLink={false}
            />
        );
    }
    if (!r.domainId) {
        return (
            <InfoPopup
                info={t("domainNotFoundDescription")}
                text={t("domainNotFound")}
            />
        );
    }
    const fullUrl = `${r.ssl ? "https" : "http"}://${toUnicode(r.fullDomain || "")}`;
    return (
        <CopyToClipboard
            text={fullUrl}
            isLink={isSafeUrlForLink(fullUrl)}
            displayText={fullUrl}
        />
    );
}

function PrivateAccessMethod({ row }: { row: SiteResourceRow }) {
    if (row.mode === "http" && row.fullDomain) {
        const url = `${row.ssl ? "https" : "http"}://${toUnicode(row.fullDomain)}`;
        return (
            <CopyToClipboard
                text={url}
                isLink={isSafeUrlForLink(url)}
                displayText={url}
            />
        );
    }
    if (row.mode === "host" && row.alias) {
        return (
            <CopyToClipboard
                text={row.alias}
                isLink={false}
                displayText={row.alias}
            />
        );
    }
    const fromAlias = row.alias?.trim();
    if (fromAlias) {
        return (
            <CopyToClipboard
                text={fromAlias}
                isLink={false}
                displayText={fromAlias}
            />
        );
    }
    const dest = formatSiteResourceDestinationDisplay({
        mode: row.mode,
        destination: row.destination,
        httpHttpsPort: row.destinationPort,
        scheme: row.scheme
    });
    return (
        <CopyToClipboard
            text={dest}
            isLink={isSafeUrlForLink(dest)}
            displayText={dest}
        />
    );
}

type OverviewRow = {
    key: number;
    meta: ReactNode;
    name: string;
    access: ReactNode;
    editHref: string;
};

type OverviewColumnProps = {
    title: string;
    description: string;
    viewAllHref: string;
    viewAllLabel: string;
    emptyLabel: string;
    isForbidden: boolean;
    isFetching: boolean;
    rows: OverviewRow[];
    canShowMore: boolean;
    onShowMore: () => void;
};

function OverviewColumn({
    title,
    description,
    viewAllHref,
    viewAllLabel,
    emptyLabel,
    isForbidden,
    isFetching,
    rows,
    canShowMore,
    onShowMore
}: OverviewColumnProps) {
    const t = useTranslations();

    const header = (
        <div className="border-b px-5 py-5">
            <div className="flex items-start justify-between gap-4">
                <div className="text-lg space-y-0.5 pb-6">
                    <h2 className="text-1xl font-semibold tracking-tight flex items-center gap-2">
                        {title}
                    </h2>
                    <p className="text-muted-foreground text-sm">
                        {description}
                    </p>
                </div>
                <Link
                    href={viewAllHref}
                    className="shrink-0 text-muted-foreground text-sm hover:underline"
                >
                    {viewAllLabel}
                </Link>
            </div>
        </div>
    );

    if (isForbidden) {
        return (
            <div className="min-w-0 overflow-hidden rounded-lg border h-full flex flex-col">
                {header}
                <p className="px-5 py-3 text-sm text-muted-foreground">
                    {t("siteResourcesPermissionDenied")}
                </p>
            </div>
        );
    }

    return (
        <div className="min-w-0 overflow-hidden rounded-lg border h-full flex flex-col">
            {header}
            {rows.length === 0 ? (
                <div className="flex flex-1 items-center justify-center px-5 py-3">
                    <p className="text-center text-sm text-muted-foreground">
                        {emptyLabel}
                    </p>
                </div>
            ) : (
                <>
                    <div className="relative flex-1">
                        <div
                            aria-hidden
                            className="pointer-events-none absolute inset-y-0 left-25 border-l border-border"
                        />
                        <ul className="relative divide-y">
                            {rows.map((row) => (
                                <li key={row.key} className="flex">
                                    <div className="w-25 min-w-0 shrink-0 px-5 py-3">
                                        {row.meta}
                                    </div>
                                    <div className="min-w-0 min-h-0 flex-1 px-5 py-3">
                                        <div className="truncate text-sm font-medium">
                                            {row.name}
                                        </div>
                                        <div className="mt-1 min-w-0 break-words text-sm text-muted-foreground">
                                            {row.access}
                                        </div>
                                    </div>
                                    <div className="flex shrink-0 items-center px-5 py-3">
                                        <Button
                                            asChild
                                            type="button"
                                            variant="outline"
                                        >
                                            <Link href={row.editHref}>
                                                {t("edit")}
                                            </Link>
                                        </Button>
                                    </div>
                                </li>
                            ))}
                        </ul>
                    </div>
                    {canShowMore ? (
                        <div className="border-t px-5 py-3 text-center">
                            <button
                                type="button"
                                onClick={onShowMore}
                                disabled={isFetching}
                                className="text-sm hover:underline text-muted-foreground cursor-pointer"
                            >
                                {isFetching
                                    ? t("loading")
                                    : t("siteResourcesShowMore")}
                            </button>
                        </div>
                    ) : null}
                </>
            )}
        </div>
    );
}

type SiteResourcesOverviewProps = {
    siteId: number;
    initialPublicData: ListResourcesResponse | null;
    initialPrivateData: ListAllSiteResourcesByOrgResponse | null;
    initialPublicForbidden: boolean;
    initialPrivateForbidden: boolean;
    /** When not under `/[orgId]/...` routes, pass org id explicitly (e.g. credenza on sites list). */
    orgIdOverride?: string;
};

export default function SiteResourcesOverview({
    siteId,
    initialPublicData,
    initialPrivateData,
    initialPublicForbidden,
    initialPrivateForbidden,
    orgIdOverride
}: SiteResourcesOverviewProps) {
    const t = useTranslations();
    const params = useParams<{ orgId: string }>();
    const orgId = orgIdOverride ?? params.orgId;
    const { env } = useEnvContext();
    const api = useMemo(() => createApiClient({ env }), [env]);

    const enabled = Boolean(orgId && siteId);

    const [publicPageSize, setPublicPageSize] = useState(INITIAL_PAGE_SIZE);
    const [privatePageSize, setPrivatePageSize] = useState(INITIAL_PAGE_SIZE);

    const publicQuery = useQuery({
        queryKey: [
            "siteResourcesOverview",
            "public",
            orgId,
            siteId,
            publicPageSize
        ] as const,
        enabled: enabled && !initialPublicForbidden,
        initialData: initialPublicData ?? undefined,
        queryFn: async (): Promise<ListResourcesResponse> => {
            const sp = new URLSearchParams({
                page: "1",
                pageSize: String(publicPageSize),
                siteId: String(siteId)
            });
            const res = await api.get(
                `/org/${orgId}/resources?${sp.toString()}`
            );
            const envelope = res.data as ResponseT<ListResourcesResponse>;
            const payload = envelope.data;
            if (!payload) {
                throw new Error("No data");
            }
            return payload;
        }
    });

    const privateQuery = useQuery({
        queryKey: [
            "siteResourcesOverview",
            "private",
            orgId,
            siteId,
            privatePageSize
        ] as const,
        enabled: enabled && !initialPrivateForbidden,
        initialData: initialPrivateData ?? undefined,
        queryFn: async (): Promise<ListAllSiteResourcesByOrgResponse> => {
            const sp = new URLSearchParams({
                page: "1",
                pageSize: String(privatePageSize),
                siteId: String(siteId)
            });
            const res = await api.get(
                `/org/${orgId}/site-resources?${sp.toString()}`
            );
            const envelope =
                res.data as ResponseT<ListAllSiteResourcesByOrgResponse>;
            const payload = envelope.data;
            if (!payload) {
                throw new Error("No data");
            }
            return payload;
        }
    });

    const publicList = publicQuery.data?.resources ?? [];
    const publicTotal = publicQuery.data?.pagination.total ?? 0;
    const privateList = privateQuery.data?.siteResources ?? [];
    const privateTotal = privateQuery.data?.pagination.total ?? 0;

    const publicForbidden =
        initialPublicForbidden ||
        (publicQuery.isError && isForbidden(publicQuery.error));
    const privateForbidden =
        initialPrivateForbidden ||
        (privateQuery.isError && isForbidden(privateQuery.error));

    const showEmptyPlaceholder =
        !publicForbidden &&
        !privateForbidden &&
        publicList.length === 0 &&
        privateList.length === 0;

    const publicViewAllHref = `/${orgId}/settings/resources/proxy?siteId=${siteId}`;
    const privateViewAllHref = `/${orgId}/settings/resources/client?siteId=${siteId}`;

    const publicRows = publicList.map((r) => ({
        key: r.resourceId,
        meta: <PublicResourceMeta resource={r} />,
        name: r.name,
        access: <PublicAccessMethod resource={r} />,
        editHref: `/${orgId}/settings/resources/proxy/${r.niceId}`
    }));

    const privateRows = privateList.map((row) => {
        const qs = new URLSearchParams({
            siteId: String(siteId),
            query: row.niceId
        });
        return {
            key: row.siteResourceId,
            meta: <PrivateResourceMeta row={row} />,
            name: row.name,
            access: <PrivateAccessMethod row={row} />,
            editHref: `/${orgId}/settings/resources/client?${qs.toString()}`
        };
    });

    if (showEmptyPlaceholder) {
        return (
            <SettingsContainer>
                <p className="pt-2 text-sm text-muted-foreground">
                    {t("siteResourcesNoneOnSite")}
                </p>
            </SettingsContainer>
        );
    }

    const publicColumn = (
        <OverviewColumn
            key="public"
            title={t("siteResourcesSectionPublic")}
            description={t("siteResourcesSectionPublicDescription")}
            viewAllHref={publicViewAllHref}
            viewAllLabel={t("siteResourcesViewAllPublic")}
            emptyLabel={t("siteResourcesEmptyPublic")}
            isForbidden={publicForbidden}
            isFetching={publicQuery.isFetching}
            rows={publicRows}
            canShowMore={publicList.length < publicTotal}
            onShowMore={() => setPublicPageSize((n) => n + LOAD_MORE_INCREMENT)}
        />
    );

    const privateColumn = (
        <OverviewColumn
            key="private"
            title={t("siteResourcesSectionPrivate")}
            description={t("siteResourcesSectionPrivateDescription")}
            viewAllHref={privateViewAllHref}
            viewAllLabel={t("siteResourcesViewAllPrivate")}
            emptyLabel={t("siteResourcesEmptyPrivate")}
            isForbidden={privateForbidden}
            isFetching={privateQuery.isFetching}
            rows={privateRows}
            canShowMore={privateList.length < privateTotal}
            onShowMore={() =>
                setPrivatePageSize((n) => n + LOAD_MORE_INCREMENT)
            }
        />
    );

    return (
        <SettingsContainer>
            <div className="grid gap-6 md:grid-cols-2">
                {publicColumn}
                {privateColumn}
            </div>
        </SettingsContainer>
    );
}
