import { CopyOutlined, DownOutlined, InfoCircleOutlined, UpOutlined } from '@ant-design/icons';
import { App, Button, Checkbox, Input, InputNumber, Select, Switch, Tooltip } from 'antd';
import { useEffect, useState } from 'react';

import { IS_CLOUD } from '../../../../constants';
import { type Database, PostgresBackupType, databaseApi } from '../../../../entity/databases';
import { ConnectionStringParser } from '../../../../entity/databases/model/postgresql/ConnectionStringParser';
import { ClipboardHelper } from '../../../../shared/lib/ClipboardHelper';
import { ToastHelper } from '../../../../shared/toast';
import { ClipboardPasteModalComponent } from '../../../../shared/ui';

interface Props {
  database: Database;

  isShowCancelButton?: boolean;
  onCancel: () => void;

  isShowBackButton: boolean;
  onBack: () => void;

  saveButtonText?: string;
  isSaveToApi: boolean;
  onSaved: (database: Database) => void;

  isShowDbName?: boolean;
  isRestoreMode?: boolean;
}

export const EditPostgreSqlSpecificDataComponent = ({
  database,

  isShowCancelButton,
  onCancel,

  isShowBackButton,
  onBack,

  saveButtonText,
  isSaveToApi,
  onSaved,
  isShowDbName = true,
  isRestoreMode = false,
}: Props) => {
  const { message } = App.useApp();

  const [editingDatabase, setEditingDatabase] = useState<Database>();
  const [isSaving, setIsSaving] = useState(false);

  const [isConnectionTested, setIsConnectionTested] = useState(false);
  const [isTestingConnection, setIsTestingConnection] = useState(false);
  const [isConnectionFailed, setIsConnectionFailed] = useState(false);

  const hasAdvancedValues =
    !!database.postgresql?.includeSchemas?.length || !!database.postgresql?.isExcludeExtensions;
  const [isShowAdvanced, setShowAdvanced] = useState(hasAdvancedValues);

  const [hasAutoAddedPublicSchema, setHasAutoAddedPublicSchema] = useState(false);

  const [isShowPasteModal, setIsShowPasteModal] = useState(false);

  const applyConnectionString = (text: string) => {
    const trimmedText = text.trim();

    if (!trimmedText) {
      message.error('Clipboard is empty');
      return;
    }

    const result = ConnectionStringParser.parse(trimmedText);

    if ('error' in result) {
      message.error(result.error);
      return;
    }

    if (!editingDatabase?.postgresql) return;

    const updatedDatabase: Database = {
      ...editingDatabase,
      postgresql: {
        ...editingDatabase.postgresql,
        host: result.host,
        port: result.port,
        username: result.username,
        password: result.password,
        database: result.database,
        isHttps: result.isHttps,
        cpuCount: 1,
      },
    };

    setEditingDatabase(autoAddPublicSchemaForSupabase(updatedDatabase));
    setIsConnectionTested(false);
    message.success('Connection string parsed successfully');
  };

  const parseFromClipboard = async () => {
    if (!ClipboardHelper.isClipboardApiAvailable()) {
      setIsShowPasteModal(true);
      return;
    }

    try {
      const text = await ClipboardHelper.readFromClipboard();
      applyConnectionString(text);
    } catch {
      message.error('Failed to read clipboard. Please check browser permissions.');
    }
  };

  const autoAddPublicSchemaForSupabase = (updatedDatabase: Database): Database => {
    if (hasAutoAddedPublicSchema) return updatedDatabase;

    const host = updatedDatabase.postgresql?.host || '';
    const username = updatedDatabase.postgresql?.username || '';
    const isSupabase = host.includes('supabase') || username.includes('supabase');

    if (isSupabase && updatedDatabase.postgresql) {
      setHasAutoAddedPublicSchema(true);

      const currentSchemas = updatedDatabase.postgresql.includeSchemas || [];
      if (!currentSchemas.includes('public')) {
        return {
          ...updatedDatabase,
          postgresql: {
            ...updatedDatabase.postgresql,
            includeSchemas: ['public', ...currentSchemas],
          },
        };
      }
    }

    return updatedDatabase;
  };

  const testConnection = async () => {
    if (!editingDatabase?.postgresql) return;
    setIsTestingConnection(true);
    setIsConnectionFailed(false);

    const trimmedDatabase = {
      ...editingDatabase,
      postgresql: {
        ...editingDatabase.postgresql,
        password: editingDatabase.postgresql.password?.trim(),
      },
    };

    try {
      await databaseApi.testDatabaseConnectionDirect(trimmedDatabase);
      setIsConnectionTested(true);
      ToastHelper.showToast({
        title: 'Connection test passed',
        description: 'You can continue with the next step',
      });
    } catch (e) {
      setIsConnectionFailed(true);
      alert((e as Error).message);
    }

    setIsTestingConnection(false);
  };

  const saveDatabase = async () => {
    if (!editingDatabase?.postgresql) return;

    const trimmedDatabase = {
      ...editingDatabase,
      postgresql: {
        ...editingDatabase.postgresql,
        password: editingDatabase.postgresql.password?.trim(),
      },
    };

    if (isSaveToApi) {
      setIsSaving(true);

      try {
        await databaseApi.updateDatabase(trimmedDatabase);
      } catch (e) {
        alert((e as Error).message);
      }

      setIsSaving(false);
    }

    onSaved(trimmedDatabase);
  };

  useEffect(() => {
    setIsSaving(false);
    setIsConnectionTested(false);
    setIsTestingConnection(false);
    setIsConnectionFailed(false);

    setEditingDatabase({ ...database });
  }, [database]);

  if (!editingDatabase) return null;

  const backupType = editingDatabase.postgresql?.backupType;

  const renderBackupTypeSelector = () => {
    if (editingDatabase.id || IS_CLOUD) return null;

    return (
      <div className="mb-3 flex w-full items-center">
        <div className="min-w-[150px]">Backup type</div>
        <Select
          value={
            backupType === PostgresBackupType.WAL_V1
              ? PostgresBackupType.WAL_V1
              : PostgresBackupType.PG_DUMP
          }
          onChange={(value: PostgresBackupType) => {
            if (!editingDatabase.postgresql) return;

            setEditingDatabase({
              ...editingDatabase,
              postgresql: { ...editingDatabase.postgresql, backupType: value },
            });
            setIsConnectionTested(false);
          }}
          options={[
            { label: 'Remote (recommended)', value: PostgresBackupType.PG_DUMP },
            { label: 'Agent (incremental)', value: PostgresBackupType.WAL_V1 },
          ]}
          size="small"
          className="min-w-[200px]"
        />
      </div>
    );
  };

  const renderFooter = (footerContent?: React.ReactNode) => (
    <div className="mt-5 flex">
      {isShowCancelButton && (
        <Button className="mr-1" danger ghost onClick={() => onCancel()}>
          Cancel
        </Button>
      )}

      {isShowBackButton && (
        <Button className="mr-auto" type="primary" ghost onClick={() => onBack()}>
          Back
        </Button>
      )}

      {footerContent}
    </div>
  );

  const renderPgDumpForm = () => {
    let isAllFieldsFilled = true;
    if (!editingDatabase.postgresql?.host) isAllFieldsFilled = false;
    if (!editingDatabase.postgresql?.port) isAllFieldsFilled = false;
    if (!editingDatabase.postgresql?.username) isAllFieldsFilled = false;
    if (!editingDatabase.id && !editingDatabase.postgresql?.password) isAllFieldsFilled = false;
    if (!editingDatabase.postgresql?.database) isAllFieldsFilled = false;

    const isLocalhostDb =
      editingDatabase.postgresql?.host?.includes('localhost') ||
      editingDatabase.postgresql?.host?.includes('127.0.0.1');

    const isSupabaseDb =
      editingDatabase.postgresql?.host?.includes('supabase') ||
      editingDatabase.postgresql?.username?.includes('supabase');

    return (
      <>
        <div className="mb-3 flex">
          <div className="min-w-[150px]" />
          <div
            className="cursor-pointer text-sm text-gray-600 transition-colors hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-200"
            onClick={parseFromClipboard}
          >
            <CopyOutlined className="mr-1" />
            Parse from clipboard
          </div>
        </div>

        <div className="mb-1 flex w-full items-center">
          <div className="min-w-[150px]">Host</div>
          <Input
            value={editingDatabase.postgresql?.host}
            onChange={(e) => {
              if (!editingDatabase.postgresql) return;

              const updatedDatabase = {
                ...editingDatabase,
                postgresql: {
                  ...editingDatabase.postgresql,
                  host: e.target.value.trim().replace('https://', '').replace('http://', ''),
                },
              };
              setEditingDatabase(autoAddPublicSchemaForSupabase(updatedDatabase));
              setIsConnectionTested(false);
            }}
            size="small"
            className="max-w-[200px] grow"
            placeholder="Enter PG host"
          />
        </div>

        {isLocalhostDb && !IS_CLOUD && (
          <div className="mb-1 flex">
            <div className="min-w-[150px]" />
            <div className="max-w-[200px] text-xs text-gray-500 dark:text-gray-400">
              Please{' '}
              <a
                href="https://databasus.com/faq/localhost"
                target="_blank"
                rel="noreferrer"
                className="!text-blue-600 dark:!text-blue-400"
              >
                read this document
              </a>{' '}
              to study how to backup local database
            </div>
          </div>
        )}

        {isSupabaseDb && (
          <div className="mb-1 flex">
            <div className="min-w-[150px]" />
            <div className="max-w-[200px] text-xs text-gray-500 dark:text-gray-400">
              Please{' '}
              <a
                href="https://databasus.com/faq/supabase"
                target="_blank"
                rel="noreferrer"
                className="!text-blue-600 dark:!text-blue-400"
              >
                read this document
              </a>{' '}
              to study how to backup Supabase database
            </div>
          </div>
        )}

        <div className="mb-1 flex w-full items-center">
          <div className="min-w-[150px]">Port</div>
          <InputNumber
            type="number"
            value={editingDatabase.postgresql?.port}
            onChange={(e) => {
              if (!editingDatabase.postgresql || e === null) return;

              setEditingDatabase({
                ...editingDatabase,
                postgresql: { ...editingDatabase.postgresql, port: e },
              });
              setIsConnectionTested(false);
            }}
            size="small"
            className="max-w-[200px] grow"
            placeholder="Enter PG port"
          />
        </div>

        <div className="mb-1 flex w-full items-center">
          <div className="min-w-[150px]">Username</div>
          <Input
            value={editingDatabase.postgresql?.username}
            onChange={(e) => {
              if (!editingDatabase.postgresql) return;

              const updatedDatabase = {
                ...editingDatabase,
                postgresql: { ...editingDatabase.postgresql, username: e.target.value.trim() },
              };
              setEditingDatabase(autoAddPublicSchemaForSupabase(updatedDatabase));
              setIsConnectionTested(false);
            }}
            size="small"
            className="max-w-[200px] grow"
            placeholder="Enter PG username"
          />
        </div>

        <div className="mb-1 flex w-full items-center">
          <div className="min-w-[150px]">Password</div>
          <Input.Password
            value={editingDatabase.postgresql?.password}
            onChange={(e) => {
              if (!editingDatabase.postgresql) return;

              setEditingDatabase({
                ...editingDatabase,
                postgresql: { ...editingDatabase.postgresql, password: e.target.value },
              });
              setIsConnectionTested(false);
            }}
            size="small"
            className="max-w-[200px] grow"
            placeholder="Enter PG password"
            autoComplete="off"
            data-1p-ignore
            data-lpignore="true"
            data-form-type="other"
          />
        </div>

        {isShowDbName && (
          <div className="mb-1 flex w-full items-center">
            <div className="min-w-[150px]">DB name</div>
            <Input
              value={editingDatabase.postgresql?.database}
              onChange={(e) => {
                if (!editingDatabase.postgresql) return;

                setEditingDatabase({
                  ...editingDatabase,
                  postgresql: { ...editingDatabase.postgresql, database: e.target.value.trim() },
                });
                setIsConnectionTested(false);
              }}
              size="small"
              className="max-w-[200px] grow"
              placeholder="Enter PG database name"
            />
          </div>
        )}

        <div className="mb-1 flex w-full items-center">
          <div className="min-w-[150px]">Use HTTPS</div>
          <Switch
            checked={editingDatabase.postgresql?.isHttps}
            onChange={(checked) => {
              if (!editingDatabase.postgresql) return;

              setEditingDatabase({
                ...editingDatabase,
                postgresql: { ...editingDatabase.postgresql, isHttps: checked },
              });
              setIsConnectionTested(false);
            }}
            size="small"
          />
        </div>

        {isRestoreMode && !IS_CLOUD && (
          <div className="mb-5 flex w-full items-center">
            <div className="min-w-[150px]">CPU count</div>
            <div className="flex items-center">
              <InputNumber
                min={1}
                max={128}
                value={editingDatabase.postgresql?.cpuCount}
                onChange={(value) => {
                  if (!editingDatabase.postgresql) return;

                  setEditingDatabase({
                    ...editingDatabase,
                    postgresql: { ...editingDatabase.postgresql, cpuCount: value || 1 },
                  });
                  setIsConnectionTested(false);
                }}
                size="small"
                className="max-w-[75px] grow"
              />

              <Tooltip
                className="cursor-pointer"
                title="Number of CPU cores to use for backup and restore operations. Higher values may speed up operations but use more resources."
              >
                <InfoCircleOutlined className="ml-2" style={{ color: 'gray' }} />
              </Tooltip>
            </div>
          </div>
        )}

        <div className="mt-4 mb-1 flex items-center">
          <div
            className="flex cursor-pointer items-center text-sm text-blue-600 hover:text-blue-800"
            onClick={() => setShowAdvanced(!isShowAdvanced)}
          >
            <span className="mr-2">Advanced settings</span>

            {isShowAdvanced ? (
              <UpOutlined style={{ fontSize: '12px' }} />
            ) : (
              <DownOutlined style={{ fontSize: '12px' }} />
            )}
          </div>
        </div>

        {isShowAdvanced && (
          <>
            {!isRestoreMode && (
              <div className="mb-1 flex w-full items-center">
                <div className="min-w-[150px]">Include schemas</div>
                <Select
                  mode="tags"
                  value={editingDatabase.postgresql?.includeSchemas || []}
                  onChange={(values) => {
                    if (!editingDatabase.postgresql) return;

                    setEditingDatabase({
                      ...editingDatabase,
                      postgresql: { ...editingDatabase.postgresql, includeSchemas: values },
                    });
                  }}
                  size="small"
                  className="max-w-[200px] grow"
                  placeholder="All schemas (default)"
                  tokenSeparators={[',']}
                />
              </div>
            )}

            {isRestoreMode && (
              <div className="mb-1 flex w-full items-center">
                <div className="min-w-[150px]">Exclude extensions</div>
                <div className="flex items-center">
                  <Checkbox
                    checked={editingDatabase.postgresql?.isExcludeExtensions || false}
                    onChange={(e) => {
                      if (!editingDatabase.postgresql) return;

                      setEditingDatabase({
                        ...editingDatabase,
                        postgresql: {
                          ...editingDatabase.postgresql,
                          isExcludeExtensions: e.target.checked,
                        },
                      });
                    }}
                  >
                    Skip extensions
                  </Checkbox>

                  <Tooltip
                    className="cursor-pointer"
                    title="Skip restoring extension definitions (CREATE EXTENSION statements). Enable this if you're restoring to a managed PostgreSQL service where extensions are managed by the provider."
                  >
                    <InfoCircleOutlined className="ml-2" style={{ color: 'gray' }} />
                  </Tooltip>
                </div>
              </div>
            )}
          </>
        )}

        {renderFooter(
          <>
            {!isConnectionTested && (
              <Button
                type="primary"
                onClick={() => testConnection()}
                loading={isTestingConnection}
                disabled={!isAllFieldsFilled}
                className="mr-5"
              >
                Test connection
              </Button>
            )}

            {isConnectionTested && (
              <Button
                type="primary"
                onClick={() => saveDatabase()}
                loading={isSaving}
                disabled={!isAllFieldsFilled}
                className="mr-5"
              >
                {saveButtonText || 'Save'}
              </Button>
            )}
          </>,
        )}

        {isConnectionFailed && !IS_CLOUD && (
          <div className="mt-3 text-sm text-gray-500 dark:text-gray-400">
            If your database uses IP whitelist, make sure Databasus server IP is added to the
            allowed list.
          </div>
        )}
      </>
    );
  };

  const renderWalForm = () => {
    return (
      <>
        <div className="mb-3 flex">
          <div className="text-sm text-gray-500 dark:text-gray-400">
            Agent mode uses physical and WAL-based incremental backups. Best suited for DBs without
            public access, for large databases (100 GB+) or when PITR is required
            <br />
            <br />
            Configuration is more complicated than remote backup and requires installing a Databasus
            agent near DB
          </div>
        </div>

        {renderFooter(
          <Button type="primary" onClick={() => saveDatabase()} loading={isSaving} className="mr-5">
            {saveButtonText || 'Save'}
          </Button>,
        )}
      </>
    );
  };

  const renderFormContent = () => {
    switch (backupType) {
      case PostgresBackupType.WAL_V1:
        return renderWalForm();
      default:
        return renderPgDumpForm();
    }
  };

  return (
    <div>
      {renderBackupTypeSelector()}
      {renderFormContent()}

      <ClipboardPasteModalComponent
        open={isShowPasteModal}
        onSubmit={(text) => {
          setIsShowPasteModal(false);
          applyConnectionString(text);
        }}
        onCancel={() => setIsShowPasteModal(false)}
      />
    </div>
  );
};
