import { useCallback } from 'react';

import {
  type RepositorySpec,
  useCreateRepositoryMutation,
  useCreateRepositoryTestMutation,
  useReplaceRepositoryMutation,
} from 'app/api/clients/provisioning/v0alpha1';

export function useCreateOrUpdateRepository(name?: string) {
  const [create, createRequest] = useCreateRepositoryMutation();
  const [update, updateRequest] = useReplaceRepositoryMutation();
  const [testConfig, testRequest] = useCreateRepositoryTestMutation();

  const updateOrCreate = useCallback(
    async (data: RepositorySpec, token?: string) => {
      const secure = token?.length ? { token: { create: token } } : undefined;

      // First test the config and wait for the result
      // unwrap will throw an error if the test fails
      await testConfig({
        // HACK: we need to provide a name to the test configuration
        name: name || 'new',
        body: {
          spec: data,
          secure,
        },
      }).unwrap();

      // If test passes, proceed with create/update
      if (name) {
        return update({
          name,
          repository: {
            metadata: {
              name,
              // TODO? -- replace with patch spec, so the rest of the metadata is not replaced?
              // Can that support optimistic locking? (eg, make sure the RV is the same?)
              finalizers: ['cleanup', 'remove-orphan-resources'],
            },
            spec: data,
            secure,
          },
        });
      }
      return create({ repository: { metadata: generateRepositoryMetadata(data), spec: data, secure } });
    },
    [create, name, update, testConfig]
  );

  return [updateOrCreate, name ? updateRequest : createRequest, testRequest] as const;
}

const generateRepositoryMetadata = (data: RepositorySpec) => {
  // We don't know for sure that we can use a normalised name. If we can't, we'll ask the server to generate one for us.
  const normalisedName = data.title.toLowerCase().replaceAll(/[^a-z0-9\-_]+/g, '');

  if (
    crypto.randomUUID && // we might not be in a secure context
    normalisedName && // we need a non-empty string before we check the first character
    normalisedName.charAt(0) >= 'a' && // required to start with a letter to be a valid k8s name
    normalisedName.charAt(0) <= 'z' &&
    normalisedName.replaceAll(/[^a-z]/g, '').length >= 3 // must look sensible to a human
  ) {
    // We still want a suffix, to avoid name collisions.
    const randomBit = crypto.randomUUID().substring(0, 7);
    const shortenedName = normalisedName.substring(0, 63 - 1 - randomBit.length);
    return { name: `${shortenedName}-${randomBit}` };
  } else {
    return { generateName: 'r' };
  }
};
