import { useState } from 'react';
import { useSearchParams } from 'react-router';
import { Loader2, ExternalLink } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { useAppStore, type ConfigField, type InstallMode, type StoreItem } from './useAppStore';

// One entry: what it is, how to install it, and what to do about it once installed.
//
// The install form is generated from the catalogue's fields rather than written per service, which is
// what lets a sidecar shipping from its own repository present a form nobody here wrote.

const modeLabel: Record<InstallMode, string> = {
  existing: 'I already have one',
  provisioned: 'Set one up for me',
  config: 'Configure',
};

const modeHelp: Record<InstallMode, string> = {
  existing: 'Point Officer at an instance you already run — here, on another machine, or anywhere reachable.',
  provisioned: 'Officer writes a compose file into your own dockers directory and starts it.',
  config: 'Nothing to install. Officer just needs the settings.',
};

function Field({ field, value, onChange }: { field: ConfigField; value: string; onChange: (v: string) => void }) {
  return (
    <label className="flex flex-col gap-1 text-sm">
      <span className="text-muted-foreground">
        {field.label}
        {!field.required && <span className="ml-1 text-xs opacity-60">(optional)</span>}
      </span>
      <input
        type={field.type === 'secret' ? 'password' : 'text'}
        value={value}
        placeholder={field.placeholder}
        onChange={(ev) => onChange(ev.target.value)}
        className="rounded-md border border-input bg-background/80 px-2 py-1.5 text-sm focus:border-duck-teal/50 focus:outline-none focus:ring-2 focus:ring-duck-teal/30"
      />
      {field.help && <span className="text-xs text-muted-foreground/80">{field.help}</span>}
    </label>
  );
}

export const AppStoreDetail = () => {
  const [params] = useSearchParams();
  const id = params.get('selected');
  const { items, install, setEnabled, uninstall } = useAppStore();
  const item = items.find((i) => i.id === id);

  const [mode, setMode] = useState<InstallMode | null>(null);
  const [values, setValues] = useState<Record<string, string>>({});
  const [log, setLog] = useState<string[]>([]);

  if (!item) {
    return <div className="flex h-full items-center justify-center text-sm text-muted-foreground">Select an app</div>;
  }

  // `existing` is first in `modes` by catalogue rule, so the default lands on "I already have one" —
  // the answer that avoids starting a second copy of something the user is already running.
  const chosen = mode ?? item.modes[0]!;
  const fields = chosen === 'existing' ? (item.existingFields ?? []) : (item.configFields ?? []);
  const isInstalled = item.install.status === 'installed' || item.install.status === 'blocked';

  const run = async (replace = false) => {
    setLog([]);
    const result = await install.mutateAsync({
      id: item.id,
      mode: chosen,
      values: replace ? { ...values, replaceConnection: 'true' } : values,
    });
    setLog(result.log ?? []);
  };

  return (
    <div className="flex h-full flex-col gap-4 overflow-y-auto p-4">
      <div>
        <h2 className="text-lg font-semibold">{item.label}</h2>
        <p className="text-sm text-muted-foreground">{item.summary}</p>
      </div>

      {/* Blocked is deliberately not styled as an error: everything worked and it is waiting for a
          person. `completeAt` links to the page that mints the token. */}
      {item.install.status === 'blocked' && item.install.lastError && (
        <div className="rounded-md border border-amber-500/40 bg-amber-500/10 p-3 text-sm">
          <div className="font-medium">Needs you</div>
          <p className="mt-1 text-muted-foreground">{item.install.lastError}</p>
        </div>
      )}
      {item.install.status === 'failed' && item.install.lastError && (
        <div className="rounded-md border-destructive/40 bg-destructive/10 border p-3 text-sm">
          <div className="font-medium">Install failed</div>
          <p className="mt-1 whitespace-pre-wrap text-muted-foreground">{item.install.lastError}</p>
        </div>
      )}

      {isInstalled ? (
        <div className="flex flex-col gap-3">
          <div className="text-sm text-muted-foreground">
            Installed {item.install.mode ? `(${item.install.mode})` : ''} ·{' '}
            {item.install.enabled ? 'enabled' : 'disabled'}
            {item.processStatus && ` · process ${item.processStatus}`}
          </div>
          <div className="flex gap-2">
            <Button
              variant="outline"
              disabled={setEnabled.isPending}
              onClick={() => setEnabled.mutate({ id: item.id, enabled: !item.install.enabled })}
            >
              {item.install.enabled ? 'Disable' : 'Enable'}
            </Button>
            <Button variant="outline" disabled={uninstall.isPending} onClick={() => uninstall.mutate(item.id)}>
              Uninstall
            </Button>
          </div>
          {/* Said plainly, because it is the question anyone hesitates over before clicking. */}
          <p className="text-xs text-muted-foreground">
            Disabling stops the sidecar and its container. Uninstalling also removes the containers — your data,
            configuration and this app’s tables are kept either way.
          </p>
        </div>
      ) : (
        <div className="flex flex-col gap-3">
          {item.modes.length > 1 && (
            <div className="flex flex-col gap-2">
              {item.modes.map((m) => (
                <label key={m} className="flex items-start gap-2 text-sm">
                  <input type="radio" checked={chosen === m} onChange={() => setMode(m)} className="mt-1" />
                  <span>
                    <span className="font-medium">{modeLabel[m]}</span>
                    <span className="block text-xs text-muted-foreground">{modeHelp[m]}</span>
                  </span>
                </label>
              ))}
            </div>
          )}

          {fields.map((field) => (
            <Field
              key={field.key}
              field={field}
              value={values[field.key] ?? ''}
              onChange={(v) => setValues((prev) => ({ ...prev, [field.key]: v }))}
            />
          ))}

          <Button className="self-start" disabled={install.isPending} onClick={() => run()}>
            {install.isPending && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
            Install
          </Button>
        </div>
      )}

      {/* The install log. Streaming it into a terminal panel is the intended shape; until then it is
          shown whole, because an install that says only "failed" is not diagnosable. */}
      {log.length > 0 && (
        <pre className="max-h-64 overflow-y-auto rounded-md bg-black/40 p-3 text-xs leading-relaxed text-foreground/80">
          {log.join('\n')}
        </pre>
      )}

      {item.members !== 'none' && (
        <p className="text-xs text-muted-foreground">
          {item.members === 'accounts'
            ? 'Members get their own account on this service automatically.'
            : 'Members are invited and set their own password — this service is end-to-end encrypted, so Officer cannot do it for them.'}
        </p>
      )}
    </div>
  );
};
