import { useState } from 'react';
import { FormProvider, useForm } from 'react-hook-form';
import { useNavigate } from 'react-router-dom-v5-compat';

import { Trans, t } from '@grafana/i18n';
import { reportInteraction } from '@grafana/runtime';
import { Alert, Button, Field, Input, Stack } from '@grafana/ui';
import { type Folder } from 'app/api/clients/folder/v1beta1';
import { type RepositoryView, useCreateRepositoryFilesWithPathMutation } from 'app/api/clients/provisioning/v0alpha1';
import { useUrlParams } from 'app/core/navigation/hooks';
import { AnnoKeySourcePath, type Resource } from 'app/features/apiserver/types';
import { usePullRequestParam } from 'app/features/provisioning/hooks/usePullRequestParam';
import { type FolderDTO } from 'app/types/folders';

import { ProvisioningAlert } from '../../Shared/ProvisioningAlert';
import { useProvisionedFolderFormData } from '../../hooks/useProvisionedFolderFormData';
import { type ProvisionedOperationInfo, useProvisionedRequestHandler } from '../../hooks/useProvisionedRequestHandler';
import { type BaseProvisionedFormData } from '../../types/form';
import { buildResourceBranchRedirectUrl } from '../../utils/redirect';
import { RepoInvalidStateBanner } from '../Shared/RepoInvalidStateBanner';
import { ResourceEditFormSharedFields } from '../Shared/ResourceEditFormSharedFields';
import { getProvisionedRequestError } from '../utils/errors';
import { joinPath } from '../utils/path';

interface FormProps extends Props {
  initialValues: BaseProvisionedFormData;
  repository?: RepositoryView;
  canPushToConfiguredBranch: boolean;
  folder?: Folder;
}
interface Props {
  parentFolder?: FolderDTO;
  onDismiss?: () => void;
}

function FormContent({ initialValues, repository, canPushToConfiguredBranch, folder, onDismiss }: FormProps) {
  const { prURL } = usePullRequestParam();
  const navigate = useNavigate();
  const [, updateUrlParams] = useUrlParams();
  const [create, request] = useCreateRepositoryFilesWithPathMutation();
  const [error, setError] = useState<string | undefined>(undefined);

  const methods = useForm<BaseProvisionedFormData>({
    defaultValues: initialValues,
    mode: 'onBlur', // Validates when user leaves the field
  });
  const { handleSubmit, watch, register, formState } = methods;

  const [workflow] = watch(['workflow']);

  const onBranchSuccess = ({ urls }: { urls?: Record<string, string> }, info: ProvisionedOperationInfo) => {
    const prUrl = urls?.newPullRequestURL;
    // Fall back to the repository URL if no PR URL is returned, so preview banner link button stay visible
    const paramValue = prUrl ?? repository?.url ?? '';
    const params: Record<string, string> = {};

    if (paramValue) {
      params.new_pull_request_url = paramValue;
    }
    if (info.repoType) {
      params.repo_type = info.repoType;
    }

    if (Object.keys(params).length > 0) {
      updateUrlParams(params);
    }
  };

  const onWriteSuccess = (resource: Resource<FolderDTO>) => {
    // Navigation for new folders (resource-specific concern)
    if (resource?.metadata?.name) {
      navigate(`/dashboards/f/${resource.metadata.name}/`);
      return;
    }

    // When sync is disabled, the BE returns resource.upsert as null (no Grafana resource created).
    // Redirect to dashboards with a banner linking to the repo status page.
    const url = buildResourceBranchRedirectUrl({
      paramName: 'resource_pushed_to',
      paramValue: repository?.name,
      repoType: repository?.type,
    });

    navigate(url);
  };

  const onError = (error: unknown) => {
    setError(
      getProvisionedRequestError(
        error,
        'folder',
        t('browse-dashboards.new-provisioned-folder-form.error-saving', 'An error occurred while creating folder.')
      )
    );
  };

  // Use the repository-type and resource-type aware provisioned request handler
  useProvisionedRequestHandler<FolderDTO>({
    folderUID: folder?.metadata.name,
    request,
    workflow,
    repository,
    resourceType: 'folder',
    selectedBranch: methods.getValues().ref,
    handlers: {
      onDismiss,
      onBranchSuccess,
      onWriteSuccess,
      onError,
    },
  });

  const doSave = async ({ ref, title, workflow, comment }: BaseProvisionedFormData) => {
    setError(undefined);
    const repoName = repository?.name;
    if (!title || !repoName) {
      onError(
        t(
          'browse-dashboards.new-provisioned-folder-form.error-missing-title-or-repo',
          'Missing folder name or repository information'
        )
      );
      return;
    }

    const basePath = folder?.metadata?.annotations?.[AnnoKeySourcePath] ?? '';
    const path = joinPath(basePath, `${title}/`);

    const folderModel = {
      title,
      type: 'folder',
    };

    if (workflow === 'write') {
      ref = undefined;
    }

    reportInteraction('grafana_provisioning_folder_create_submitted', {
      workflow,
      repositoryName: repoName,
      repositoryType: repository?.type ?? 'unknown',
    });

    create({
      ref,
      name: repoName,
      path,
      message: comment || `Create folder: ${title}`,
      body: folderModel,
    });
  };

  return (
    <FormProvider {...methods}>
      <form onSubmit={handleSubmit(doSave)}>
        <Stack direction="column" gap={2}>
          {!repository?.workflows?.length && (
            <Alert
              title={t(
                'browse-dashboards.new-provisioned-folder-form.title-this-repository-is-read-only',
                'This repository is read only'
              )}
            >
              <Trans i18nKey="browse-dashboards.text-this-repository-is-read-only">
                If you have direct access to the target, copy the JSON and paste it there.
              </Trans>
            </Alert>
          )}

          <Field
            noMargin
            label={t('browse-dashboards.new-provisioned-folder-form.label-folder-name', 'Folder name')}
            invalid={!!formState?.errors.title}
            error={formState?.errors.title?.message}
          >
            <Input
              {...register('title', {
                required: t('browse-dashboards.new-provisioned-folder-form.error-required', 'Folder name is required'),
                validate: validateProvisionedFolderName,
              })}
              placeholder={t(
                'browse-dashboards.new-provisioned-folder-form.folder-name-input-placeholder-enter-folder-name',
                'Enter folder name'
              )}
              id="folder-name-input"
            />
          </Field>

          <ResourceEditFormSharedFields
            resourceType="folder"
            isNew
            canPushToConfiguredBranch={canPushToConfiguredBranch}
            repository={repository}
            hiddenFields={['path']}
          />

          {prURL && (
            <Alert
              severity="info"
              title={t(
                'browse-dashboards.new-provisioned-folder-form.title-pull-request-created',
                'Pull request created'
              )}
            >
              <Trans i18nKey="browse-dashboards.new-provisioned-folder-form.text-pull-request-created">
                A pull request has been created with changes to this folder:
              </Trans>{' '}
              <a href={prURL} target="_blank" rel="noopener noreferrer">
                {prURL}
              </a>
            </Alert>
          )}

          {error && <ProvisioningAlert error={error} />}

          <Stack gap={2}>
            <Button variant="secondary" fill="outline" onClick={onDismiss}>
              <Trans i18nKey="browse-dashboards.new-provisioned-folder-form.cancel">Cancel</Trans>
            </Button>
            <Button type="submit" disabled={request.isLoading || !!formState?.errors.title}>
              {request.isLoading
                ? t('browse-dashboards.new-provisioned-folder-form.button-creating', 'Creating...')
                : t('browse-dashboards.new-provisioned-folder-form.button-create', 'Create')}
            </Button>
          </Stack>
        </Stack>
      </form>
    </FormProvider>
  );
}

export function NewProvisionedFolderForm({ parentFolder, onDismiss }: Props) {
  const { canPushToConfiguredBranch, repository, folder, initialValues, isReadOnlyRepo } = useProvisionedFolderFormData(
    {
      folderUid: parentFolder?.uid,
      title: '', // Empty title for new folders
    }
  );

  if (isReadOnlyRepo || !initialValues) {
    return (
      <RepoInvalidStateBanner
        noRepository={!initialValues}
        isReadOnlyRepo={isReadOnlyRepo}
        readOnlyMessage={t(
          'browse-dashboards.new-folder.read-only-message',
          'To create this folder, please add the resource in your repository directly.'
        )}
      />
    );
  }

  return (
    <FormContent
      parentFolder={parentFolder}
      onDismiss={onDismiss}
      initialValues={initialValues}
      repository={repository}
      canPushToConfiguredBranch={canPushToConfiguredBranch}
      folder={folder}
    />
  );
}

function validateProvisionedFolderName(folderName: string): string | true {
  if (!folderName || typeof folderName !== 'string') {
    return t('browse-dashboards.new-provisioned-folder-form.error-required', 'Folder name is required');
  }

  // Backend allows: a-zA-Z0-9 _- (no dots, no forward slash for folder names)
  const invalidCharRegex = /[^a-zA-Z0-9 _-]/;

  if (invalidCharRegex.test(folderName)) {
    return t(
      'browse-dashboards.new-provisioned-folder-form.error-invalid-characters',
      'Folder name contains invalid characters. Only letters, numbers, spaces, underscores, and hyphens are allowed.'
    );
  }

  return true; // Valid
}
