<script setup lang="ts">
import { client as rpcClient } from '@/api/rpcclient';
import AppButton from '@/components/ui/AppButton.vue';
import AppDialog from '@/components/ui/AppDialog.vue';
import AppInput from '@/components/ui/AppInput.vue';
import BaseTable from '@/components/ui/BaseTable.vue';
import CheckIcon from '@/components/icons/CheckIcon.vue';
import LinkIcon from '@/components/icons/LinkIcon.vue';
import PlusIcon from '@/components/icons/PlusIcon.vue';
import TrashIcon from '@/components/icons/TrashIcon.vue';
import { useAppConfirm } from '@/composables/useAppConfirm';
import { useAppToast } from '@/composables/useAppToast';
import SettingsNotice from '@/routes/settings/components/SettingsNotice.vue';
import SettingsSectionCard from '@/routes/settings/components/SettingsSectionCard.vue';
import SettingsTableSkeleton from '@/routes/settings/components/SettingsTableSkeleton.vue';
import { useQuery } from '@pinia/colada';
import type { ColumnDef } from '@tanstack/vue-table';
import { useTranslation } from 'i18next-vue';
import { computed, ref, watch } from 'vue';

const toast = useAppToast();
const confirm = useAppConfirm();
const { t } = useTranslation();

type DomainApiItem = {
    id?: string;
    name?: string;
    created_at?: string;
};

type DomainItem = {
    id: string;
    name: string;
    addedAt: string;
};

const newDomain = ref('');
const showAddDialog = ref(false);
const adding = ref(false);
const removingId = ref<string | null>(null);

const normalizeDomainInput = (value: string) => value
    .trim()
    .toLowerCase()
    .replace(/^https?:\/\//, '')
    .replace(/^www\./, '')
    .replace(/\/$/, '');

const formatDate = (value?: string) => {
    if (!value) return '-';

    const date = new Date(value);
    if (Number.isNaN(date.getTime())) {
        return value.split('T')[0] || value;
    }

    return date.toISOString().split('T')[0];
};

const mapDomainItem = (item: DomainApiItem): DomainItem => ({
    id: item.id || `${item.name || 'domain'}:${item.created_at || ''}`,
    name: item.name || '',
    addedAt: formatDate(item.created_at),
});

const { data: domainsSnapshot, error, isPending, refetch } = useQuery({
    key: () => ['settings', 'domains'],
    query: async () => {
        const response = await rpcClient.listDomains();
        return (response.domains || []).map(mapDomainItem);
    },
});

const domains = computed(() => domainsSnapshot.value || []);
const isInitialLoading = computed(() => isPending.value && !domainsSnapshot.value);

const iframeCode = computed(() => '<iframe src="https://holistream.com/embed" width="100%" height="500" frameborder="0" allowfullscreen></iframe>');

const refetchDomains = () => refetch((fetchError) => {
    throw fetchError;
});

watch(error, (value, previous) => {
    if (!value || value === previous || adding.value || removingId.value !== null) return;

    toast.add({
        severity: 'error',
        summary: t('settings.domainsDns.toast.failedSummary'),
        detail: (value as any)?.message || t('settings.domainsDns.toast.failedDetail'),
        life: 5000,
    });
});

const openAddDialog = () => {
    newDomain.value = '';
    showAddDialog.value = true;
};

const closeAddDialog = () => {
    showAddDialog.value = false;
    newDomain.value = '';
};

const handleAddDomain = async () => {
    if (adding.value) return;

    const domainName = normalizeDomainInput(newDomain.value);
    if (!domainName || !domainName.includes('.') || /[\/\s]/.test(domainName)) {
        toast.add({
            severity: 'error',
            summary: t('settings.domainsDns.toast.invalidSummary'),
            detail: t('settings.domainsDns.toast.invalidDetail'),
            life: 3000,
        });
        return;
    }

    const exists = domains.value.some(domain => domain.name === domainName);
    if (exists) {
        toast.add({
            severity: 'error',
            summary: t('settings.domainsDns.toast.duplicateSummary'),
            detail: t('settings.domainsDns.toast.duplicateDetail'),
            life: 3000,
        });
        return;
    }

    adding.value = true;
    try {
        await rpcClient.createDomain({
            name: domainName,
        });

        await refetchDomains();
        closeAddDialog();
        toast.add({
            severity: 'success',
            summary: t('settings.domainsDns.toast.addedSummary'),
            detail: t('settings.domainsDns.toast.addedDetail', { domain: domainName }),
            life: 3000,
        });
    } catch (e: any) {
        console.error(e);
        const message = String(e?.message || '').toLowerCase();

        if (message.includes('already exists')) {
            toast.add({
                severity: 'error',
                summary: t('settings.domainsDns.toast.duplicateSummary'),
                detail: t('settings.domainsDns.toast.duplicateDetail'),
                life: 3000,
            });
        } else if (message.includes('invalid domain')) {
            toast.add({
                severity: 'error',
                summary: t('settings.domainsDns.toast.invalidSummary'),
                detail: t('settings.domainsDns.toast.invalidDetail'),
                life: 3000,
            });
        } else {
            toast.add({
                severity: 'error',
                summary: t('settings.domainsDns.toast.failedSummary'),
                detail: e.message || t('settings.domainsDns.toast.failedDetail'),
                life: 5000,
            });
        }
    } finally {
        adding.value = false;
    }
};

const handleRemoveDomain = (domain: DomainItem) => {
    confirm.require({
        message: t('settings.domainsDns.confirm.removeMessage', { domain: domain.name }),
        header: t('settings.domainsDns.confirm.removeHeader'),
        acceptLabel: t('settings.domainsDns.confirm.removeAccept'),
        rejectLabel: t('settings.domainsDns.confirm.removeReject'),
        accept: async () => {
            removingId.value = domain.id;
            try {
                await rpcClient.deleteDomain({ id: domain.id });
                await refetchDomains();
                toast.add({
                    severity: 'info',
                    summary: t('settings.domainsDns.toast.removedSummary'),
                    detail: t('settings.domainsDns.toast.removedDetail', { domain: domain.name }),
                    life: 3000,
                });
            } catch (e: any) {
                console.error(e);
                toast.add({
                    severity: 'error',
                    summary: t('settings.domainsDns.toast.failedSummary'),
                    detail: e.message || t('settings.domainsDns.toast.failedDetail'),
                    life: 5000,
                });
            } finally {
                removingId.value = null;
            }
        },
    });
};

const copyIframeCode = async () => {
    try {
        await navigator.clipboard.writeText(iframeCode.value);
    } catch {
        const textArea = document.createElement('textarea');
        textArea.value = iframeCode.value;
        document.body.appendChild(textArea);
        textArea.select();
        document.execCommand('copy');
        document.body.removeChild(textArea);
    }

    toast.add({
        severity: 'success',
        summary: t('settings.domainsDns.toast.copiedSummary'),
        detail: t('settings.domainsDns.toast.copiedDetail'),
        life: 2000,
    });
};

const columns = computed<ColumnDef<DomainItem>[]>(() => [
    {
        id: 'domain',
        header: t('settings.domainsDns.table.domain'),
        accessorFn: row => row.name,
        cell: ({ row }) => h('div', { class: 'flex items-center gap-2' }, [
            h(LinkIcon, { class: 'h-4 w-4 text-foreground/40' }),
            h('span', { class: 'text-sm font-medium text-foreground' }, row.original.name),
        ]),
        meta: {
            headerClass: 'px-6 py-3 text-left text-xs font-medium uppercase tracking-wider text-foreground/50',
            cellClass: 'px-6 py-3',
        },
    },
    {
        id: 'addedAt',
        header: t('settings.domainsDns.table.addedDate'),
        accessorFn: row => row.addedAt,
        cell: ({ row }) => h('span', { class: 'text-sm text-foreground/60' }, row.original.addedAt),
        meta: {
            headerClass: 'px-6 py-3 text-left text-xs font-medium uppercase tracking-wider text-foreground/50',
            cellClass: 'px-6 py-3',
        },
    },
    {
        id: 'actions',
        header: t('common.actions'),
        enableSorting: false,
        cell: ({ row }) => h(AppButton, {
            variant: 'ghost',
            size: 'sm',
            disabled: adding.value || removingId.value !== null,
            onClick: () => handleRemoveDomain(row.original),
        }, {
            icon: () => h(TrashIcon, { class: 'h-4 w-4 text-danger' }),
        }),
        meta: {
            headerClass: 'px-6 py-3 text-right text-xs font-medium uppercase tracking-wider text-foreground/50',
            cellClass: 'px-6 py-3 text-right',
        },
    },
]);
</script>

<template>
    <SettingsSectionCard
        :title="t('settings.content.domains.title')"
        :description="t('settings.content.domains.subtitle')"
        bodyClass=""
    >
        <template #header-actions>
            <AppButton size="sm" :loading="adding" :disabled="isInitialLoading || removingId !== null" @click="openAddDialog">
                <template #icon>
                    <PlusIcon class="w-4 h-4" />
                </template>
                {{ t('settings.domainsDns.addDomain') }}
            </AppButton>
        </template>

        <SettingsNotice class="rounded-none border-x-0 border-t-0 p-3" contentClass="text-xs text-foreground/70">
            {{ t('settings.domainsDns.infoBanner') }}
        </SettingsNotice>

        <SettingsTableSkeleton v-if="isInitialLoading" :columns="3" :rows="4" />

        <BaseTable
            v-else
            :data="domains"
            :columns="columns"
            :get-row-id="(row) => row.id"
            wrapperClass="mt-4 border-b border-border rounded-none border-x-0 border-t-0 bg-transparent"
            tableClass="w-full"
            headerRowClass="bg-muted/30"
            bodyRowClass="border-b border-border hover:bg-muted/30"
        >
            <template #empty>
                <div class="px-6 py-12 text-center">
                    <LinkIcon class="mx-auto mb-3 block h-10 w-10 text-foreground/30" />
                    <p class="mb-1 text-sm text-foreground/60">{{ t('settings.domainsDns.emptyTitle') }}</p>
                    <p class="text-xs text-foreground/40">{{ t('settings.domainsDns.emptySubtitle') }}</p>
                </div>
            </template>
        </BaseTable>

        <div class="px-6 py-4 bg-muted/30">
            <div class="flex items-center justify-between mb-3">
                <h4 class="text-sm font-medium text-foreground">{{ t('settings.domainsDns.embedCodeTitle') }}</h4>
                <AppButton variant="secondary" size="sm" @click="copyIframeCode">
                    <template #icon>
                        <CheckIcon class="w-4 h-4" />
                    </template>
                    {{ t('settings.domainsDns.copyCode') }}
                </AppButton>
            </div>
            <p class="text-xs text-foreground/60 mb-2">
                {{ t('settings.domainsDns.embedCodeHint') }}
            </p>
            <pre class="bg-header border border-border rounded-md p-3 text-xs text-foreground/70 overflow-x-auto"><code>{{ iframeCode }}</code></pre>
        </div>

        <AppDialog
            :visible="showAddDialog"
            :title="t('settings.domainsDns.dialog.title')"
            maxWidthClass="max-w-md"
            @update:visible="showAddDialog = $event"
            @close="closeAddDialog"
        >
            <div class="space-y-4">
                <div class="grid gap-2">
                    <label for="domain" class="text-sm font-medium text-foreground">{{ t('settings.domainsDns.dialog.domainLabel') }}</label>
                    <AppInput
                        id="domain"
                        v-model="newDomain"
                        :placeholder="t('settings.domainsDns.dialog.domainPlaceholder')"
                        @enter="handleAddDomain"
                    />
                    <p class="text-xs text-foreground/50">{{ t('settings.domainsDns.dialog.domainHint') }}</p>
                </div>

                <SettingsNotice
                    tone="warning"
                    :title="t('settings.domainsDns.dialog.importantTitle')"
                    class="p-3"
                >
                    <p>{{ t('settings.domainsDns.dialog.importantDetail') }}</p>
                </SettingsNotice>
            </div>

            <template #footer>
                <div class="flex justify-end gap-2">
                <AppButton variant="secondary" size="sm" :disabled="adding" @click="closeAddDialog">
                    {{ t('common.cancel') }}
                </AppButton>
                <AppButton size="sm" :loading="adding" @click="handleAddDomain">
                    <template #icon>
                        <CheckIcon class="w-4 h-4" />
                    </template>
                    {{ t('settings.domainsDns.addDomain') }}
                </AppButton>
                </div>
            </template>
        </AppDialog>
    </SettingsSectionCard>
</template>
