import type { CallTemplateFunctionArgs, Context, PluginDefinition } from '@yaakapp/api';
import slugify from 'slugify';

const STORE_NONE = 'none';
const STORE_FOREVER = 'forever';
const STORE_EXPIRE = 'expire';

interface Saved {
  value: string;
  createdAt: number;
}

export const plugin: PluginDefinition = {
  templateFunctions: [
    {
      name: 'prompt.text',
      description: 'Prompt the user for input when sending a request',
      previewType: 'click',
      previewArgs: ['label'],
      args: [
        {
          type: 'text',
          name: 'label',
          label: 'Label',
          optional: true,
          dynamic(_ctx, args) {
            if (
              args.values.store === STORE_EXPIRE ||
              (args.values.store === STORE_FOREVER && !args.values.key)
            ) {
              return { optional: false };
            }
          },
        },
        {
          type: 'select',
          name: 'store',
          label: 'Store Input',
          defaultValue: STORE_NONE,
          options: [
            { label: 'Never', value: STORE_NONE },
            { label: 'Expire', value: STORE_EXPIRE },
            { label: 'Forever', value: STORE_FOREVER },
          ],
        },
        {
          type: 'h_stack',
          dynamic(_ctx, args) {
            return { hidden: args.values.store === STORE_NONE };
          },
          inputs: [
            {
              type: 'text',
              name: 'namespace',
              label: 'Namespace',
              // biome-ignore lint/suspicious/noTemplateCurlyInString: Yaak template syntax
              defaultValue: '${[ctx.workspace()]}',
              optional: true,
            },
            {
              type: 'text',
              name: 'key',
              label: 'Key (defaults to Label)',
              optional: true,
              dynamic(_ctx, args) {
                return { placeholder: String(args.values.label || '') };
              },
            },
            {
              type: 'text',
              name: 'ttl',
              label: 'TTL (seconds)',
              placeholder: '0',
              defaultValue: '0',
              optional: true,
              dynamic(_ctx, args) {
                return { hidden: args.values.store !== STORE_EXPIRE };
              },
            },
          ],
        },
        {
          type: 'banner',
          color: 'info',
          inputs: [],
          dynamic(_ctx, args) {
            let key: string;
            try {
              key = buildKey(args);
            } catch (err) {
              return { color: 'danger', inputs: [{ type: 'markdown', content: String(err) }] };
            }
            return {
              hidden: args.values.store === STORE_NONE,
              inputs: [
                {
                  type: 'markdown',
                  content: [`Value will be saved under: \`${key}\``].join('\n\n'),
                },
              ],
            };
          },
        },
        {
          type: 'accordion',
          label: 'Advanced',
          inputs: [
            {
              type: 'text',
              name: 'title',
              label: 'Prompt Title',
              optional: true,
              placeholder: 'Enter Value',
            },
            { type: 'text', name: 'defaultValue', label: 'Default Value', optional: true },
            { type: 'text', name: 'placeholder', label: 'Input Placeholder', optional: true },
            { type: 'checkbox', name: 'password', label: 'Mask Value' },
          ],
        },
      ],
      async onRender(ctx: Context, args: CallTemplateFunctionArgs): Promise<string | null> {
        if (args.purpose !== 'send') return null;

        if (args.values.store !== STORE_NONE && !args.values.namespace) {
          throw new Error('Namespace is required when storing values');
        }

        const existing = await maybeGetValue(ctx, args);
        if (existing != null) {
          return existing;
        }

        const value = await ctx.prompt.text({
          id: `prompt-${args.values.label ?? 'none'}`,
          label: String(args.values.label || 'Value'),
          title: String(args.values.title ?? 'Enter Value'),
          defaultValue: String(args.values.defaultValue ?? ''),
          placeholder: String(args.values.placeholder ?? ''),
          password: Boolean(args.values.password),
          required: false,
        });

        if (value == null) {
          throw new Error('Prompt cancelled');
        }

        if (args.values.store !== STORE_NONE) {
          await maybeSetValue(ctx, args, value);
        }

        return value;
      },
    },
  ],
};

function buildKey(args: CallTemplateFunctionArgs) {
  if (!args.values.key && !args.values.label) {
    throw new Error('A label or key is required when storing values');
  }
  return [args.values.namespace, args.values.key || args.values.label]
    .filter((v) => !!v)
    .map((v) => slugify(String(v), { lower: true, trim: true }))
    .join('.');
}

async function maybeGetValue(ctx: Context, args: CallTemplateFunctionArgs) {
  if (args.values.store === STORE_NONE) return null;

  const existing = await ctx.store.get<Saved>(buildKey(args));
  if (existing == null) {
    return null;
  }

  if (args.values.store === STORE_FOREVER) {
    return existing.value;
  }

  const ttlSeconds = Number.parseInt(String(args.values.ttl), 10) || 0;
  const ageSeconds = (Date.now() - existing.createdAt) / 1000;
  if (ageSeconds > ttlSeconds) {
    ctx.store.delete(buildKey(args)).catch(console.error);
    return null;
  }

  return existing.value;
}

async function maybeSetValue(ctx: Context, args: CallTemplateFunctionArgs, value: string) {
  if (args.values.store === STORE_NONE) {
    return;
  }

  await ctx.store.set<Saved>(buildKey(args), { value, createdAt: Date.now() });
}
