import React, { useCallback, useEffect, useMemo, useState } from 'react';
import FlashMessageRender from '@/components/FlashMessageRender';
import ServerContentBlock from '@/components/elements/ServerContentBlock';
import { Button } from '@/components/elements/button/index';
import { Dialog } from '@/components/elements/dialog';
import { ServerContext } from '@/state/server';
import useFlash, { useFlashKey } from '@/plugins/useFlash';
import {
  createProfile,
  deleteProfile,
  getImportProgress,
  getProfiles,
  getStatistics,
  startImport,
  startImportSelected,
  testConnection,
  updateProfile,
} from '../../api/server/importer';
import type { ImportProgressSnapshot } from '../../api/server/importer';
import { ImporterProfile } from '../../api/server/importer/profiles';
import { ImporterStatistics } from '../../api/server/importer/statistics';
import {
  ProfileDraft,
  createDefaultDraft,
  parseImporterHostInput,
  protocolDefaults,
  toConnectionPayload,
  toDraft,
  toImportPayload,
  toProfilePayload,
} from './types';
import { ImporterForm, ProfileSidebar, RemoteBrowserDialog } from './elements';

const FLASH_KEY = 'server:importer';
const connectionDraftKeys: Array<keyof ProfileDraft> = [
  'protocol',
  'auth_method',
  'hostname',
  'port',
  'username',
  'password',
  'ssh_key',
  'ssh_key_passphrase',
];

export default () => {
  const serverId = ServerContext.useStoreState((state) => state.server.data!.id);
  const uuid = ServerContext.useStoreState((state) => state.server.data!.uuid);
  const { addError, clearAndAddHttpError, clearFlashes } = useFlashKey(FLASH_KEY);
  const { addFlash } = useFlash();

  const [profiles, setProfiles] = useState<ImporterProfile[]>([]);
  const [statistics, setStatistics] = useState<ImporterStatistics | null>(null);
  const [draft, setDraft] = useState<ProfileDraft>(createDefaultDraft());
  const [editingId, setEditingId] = useState<number | null>(null);
  const [selectedItems, setSelectedItems] = useState<string[]>([]);
  const [loading, setLoading] = useState(true);
  const [testing, setTesting] = useState(false);
  const [saving, setSaving] = useState(false);
  const [deleting, setDeleting] = useState(false);
  const [importing, setImporting] = useState(false);
  const [lastImportProgress, setLastImportProgress] = useState<ImportProgressSnapshot | null>(null);
  const [browserOpen, setBrowserOpen] = useState(false);
  const [browserMode, setBrowserMode] = useState<'path' | 'items'>('path');
  const [deleteTarget, setDeleteTarget] = useState<ImporterProfile | null>(null);

  const connectionPayload = useMemo(() => toConnectionPayload(draft), [draft]);
  const visibleImportProgress = lastImportProgress?.status === 'completed' ? null : lastImportProgress;
  const redirectToConsole = () => {
    window.location.assign(`/server/${serverId}`);
  };

  const loadData = useCallback(async () => {
    setLoading(true);

    try {
      const [loadedProfiles, loadedStatistics, progress] = await Promise.all([
        getProfiles(uuid),
        getStatistics(uuid),
        getImportProgress(uuid),
      ]);

      setProfiles(loadedProfiles);
      setStatistics(loadedStatistics);
      if (progress.progress && progress.progress.status !== 'completed') {
        setLastImportProgress(progress.progress);
      } else {
        setLastImportProgress(null);
      }
      setImporting(progress.is_importing);
    } catch (e: any) {
      clearAndAddHttpError(e);
    } finally {
      setLoading(false);
    }
  }, [uuid]);

  useEffect(() => {
    loadData();
  }, [loadData]);

  const setDraftValue = <K extends keyof ProfileDraft>(key: K, value: ProfileDraft[K]) => {
    setDraft((current) => ({ ...current, [key]: value }));
  };

  const setFormValue = <K extends keyof ProfileDraft>(key: K, value: ProfileDraft[K]) => {
    if (key !== 'hostname') {
      setDraft((current) => ({
        ...current,
        [key]: value,
        ...(connectionDraftKeys.includes(key) ? { profile_id: null } : {}),
        ...(key === 'password' ? { has_password: false } : {}),
        ...(key === 'ssh_key' ? { has_ssh_key: false } : {}),
        ...(key === 'ssh_key_passphrase' ? { has_ssh_key_passphrase: false } : {}),
      }));

      return;
    }

    const parsed = parseImporterHostInput(String(value));
    setDraft((current) => ({
      ...current,
      profile_id: null,
      hostname: parsed.hostname ?? String(value),
      port: parsed.port ?? current.port,
      protocol: parsed.protocol ?? current.protocol,
      auth_method: parsed.protocol === 'ftp' ? 'password' : current.auth_method,
    }));
  };

  const setProtocol = (protocol: ProfileDraft['protocol']) => {
    setDraft((current) => ({
      ...current,
      profile_id: null,
      protocol,
      port: protocolDefaults[protocol],
      auth_method: protocol === 'ftp' ? 'password' : current.auth_method,
    }));
  };

  const setImportType = (importType: ProfileDraft['import_type']) => {
    setSelectedItems([]);
    setDraftValue('import_type', importType);
  };

  const clearForm = useCallback(() => {
    setDraft(createDefaultDraft());
    setEditingId(null);
    setSelectedItems([]);
    clearFlashes();
  }, [clearFlashes]);

  const selectProfile = (profile: ImporterProfile) => {
    setDraft(toDraft(profile));
    setEditingId(profile.id);
    setSelectedItems([]);
    clearFlashes();
  };

  const requireConnectionFields = () => {
    if (!draft.hostname.trim() || !draft.username.trim()) {
      addError('Hostname and username are required.');

      return false;
    }

    if (draft.auth_method === 'password' && !draft.password.trim() && !draft.has_password) {
      addError('Password is required.');

      return false;
    }

    if (draft.auth_method === 'ssh_key' && !draft.ssh_key.trim() && !draft.has_ssh_key) {
      addError('SSH private key is required when using SSH key authentication.');

      return false;
    }

    return true;
  };

  const handleTestConnection = async () => {
    if (!requireConnectionFields()) return;

    clearFlashes();
    setTesting(true);

    try {
      const result = await testConnection(uuid, connectionPayload);

      if (result.success) {
        addFlash({
          key: FLASH_KEY,
          type: 'success',
          title: 'Success',
          message: result.message || 'Connection successful.',
        });
      } else {
        addError(result.message || 'Connection test failed.');
      }
    } catch (e: any) {
      clearAndAddHttpError(e);
    } finally {
      setTesting(false);
    }
  };

  const handleStartImport = async () => {
    if (!requireConnectionFields()) return;

    clearFlashes();
    setImporting(true);
    setLastImportProgress({
      current_file: '',
      mode: draft.progress_mode,
      status: 'installing',
      processed_files: 0,
      total_files: -1,
      processed_bytes: 0,
      total_bytes: -1,
      percentage: 0,
    });

    try {
      const payload = toImportPayload(draft);
      const response =
        draft.import_type === 'selected_path' && selectedItems.length > 0
          ? await startImportSelected(uuid, { ...payload, selected_items: selectedItems })
          : await startImport(uuid, payload);

      if (!response.success) {
        const message = response.message || 'The import could not be started.';

        addError(message);
        setImporting(false);
        setLastImportProgress((current) => (current ? { ...current, status: 'failed', error: message } : current));
      } else if (response.progress) {
        setLastImportProgress(response.progress);
        const stillImporting = response.progress.status !== 'completed' && response.progress.status !== 'failed';
        setImporting(stillImporting);
        if (stillImporting) {
          redirectToConsole();
        }
      } else {
        redirectToConsole();
      }
    } catch (e: any) {
      clearAndAddHttpError(e);
      setImporting(false);
      setLastImportProgress((current) =>
        current ? { ...current, status: 'failed', error: e?.message || 'The import could not be started.' } : current
      );
    }
  };

  const handleSaveProfile = async () => {
    if (!draft.name.trim()) {
      addError('Profile name is required.');

      return;
    }

    if (!requireConnectionFields()) return;

    clearFlashes();
    setSaving(true);

    try {
      const payload = toProfilePayload(draft);

      if (editingId) {
        const updated = await updateProfile(uuid, editingId, payload);
        setProfiles((current) => current.map((profile) => (profile.id === editingId ? updated : profile)));
      } else {
        const created = await createProfile(uuid, payload);
        setProfiles((current) => [created, ...current]);
        setEditingId(created.id);
      }

      const updatedStatistics = await getStatistics(uuid);
      setStatistics(updatedStatistics);
    } catch (e: any) {
      clearAndAddHttpError(e);
    } finally {
      setSaving(false);
    }
  };

  const handleDeleteProfile = async () => {
    if (!deleteTarget) return;

    setDeleting(true);
    clearFlashes();

    try {
      await deleteProfile(uuid, deleteTarget.id);
      setProfiles((current) => current.filter((profile) => profile.id !== deleteTarget.id));

      if (editingId === deleteTarget.id) {
        clearForm();
      }

      const updatedStatistics = await getStatistics(uuid);
      setStatistics(updatedStatistics);
      setDeleteTarget(null);
    } catch (e: any) {
      clearAndAddHttpError(e);
    } finally {
      setDeleting(false);
    }
  };

  const openBrowser = () => {
    if (!requireConnectionFields()) return;

    setBrowserMode(draft.import_type === 'selected_path' ? 'items' : 'path');
    setBrowserOpen(true);
  };

  return (
    <ServerContentBlock title={'Server Importer'}>
      <FlashMessageRender byKey={FLASH_KEY} />

      <div className={'mt-4 grid gap-4 lg:grid-cols-3'}>
        <div className={'space-y-4 lg:col-span-1'}>
          <ProfileSidebar
            profiles={profiles}
            selectedId={editingId}
            statistics={statistics}
            importProgress={visibleImportProgress}
            loading={loading}
            onNew={clearForm}
            onRefresh={loadData}
            onSelect={selectProfile}
            onDelete={setDeleteTarget}
          />
        </div>

        <div className={'lg:col-span-2'}>
          <ImporterForm
            draft={draft}
            editingId={editingId}
            selectedItems={selectedItems}
            importing={importing}
            testing={testing}
            saving={saving}
            onChange={setFormValue}
            onProtocolChange={setProtocol}
            onImportTypeChange={setImportType}
            onBrowse={openBrowser}
            onTestConnection={handleTestConnection}
            onSaveProfile={handleSaveProfile}
            onStartImport={handleStartImport}
          />
        </div>
      </div>

      <RemoteBrowserDialog
        open={browserOpen}
        uuid={uuid}
        connectionData={connectionPayload}
        selectMode={browserMode}
        onClose={() => setBrowserOpen(false)}
        onSelectPath={(path) =>
          setDraft((current) => ({
            ...current,
            source_directory: path,
            import_type: current.import_type === 'full_server' ? 'directory_only' : current.import_type,
          }))
        }
        onSelectItems={(items, basePath) => {
          setSelectedItems(items);
          setDraft((current) => ({
            ...current,
            source_directory: items.length > 0 ? basePath : current.source_directory,
          }));
        }}
      />

      <Dialog
        open={deleteTarget !== null}
        onClose={() => setDeleteTarget(null)}
        title={'Delete Import Profile'}
        description={`Delete "${deleteTarget?.name}"? This saved connection will be removed from the panel.`}
        preventExternalClose={deleting}
      >
        <Dialog.Footer>
          <Button.Text type={'button'} onClick={() => setDeleteTarget(null)} disabled={deleting}>
            Cancel
          </Button.Text>
          <Button.Danger type={'button'} onClick={handleDeleteProfile} disabled={deleting}>
            {deleting ? 'Deleting...' : 'Delete'}
          </Button.Danger>
        </Dialog.Footer>
      </Dialog>
    </ServerContentBlock>
  );
};
