import { useEffect, useRef, useState } from 'react';
import type { FocusEvent, FormEvent, TouchEvent } from 'react';
import { CalendarClock, SendHorizontal, SlidersHorizontal } from 'lucide-react';
import type {
  BranchOption,
  Environment,
  ModelChoice,
  ReasoningLevel,
  RepositoryOption,
  Skill,
  Snippet,
  SkillInvocationRef,
  ScheduledFollowUpSchedule,
  ScheduledFollowUpPreview,
} from '../../api.js';
import { cn } from '../../lib/utils.js';
import { Button } from '../ui/button.js';
import { Card } from '../ui/card.js';
import { Textarea } from '../ui/textarea.js';
import {
  EnvironmentBranchOverridesEditor,
  type EnvironmentBranchOverrides,
  type EnvironmentBranchOverrideRepository,
  type EnvironmentBranchOverrideTarget,
} from './environment-branch-overrides.js';
import {
  BranchPicker,
  CodebasePicker,
  codebaseEnvironmentValue,
  codebaseRepositoryValue,
  OptionPicker,
} from './option-picker.js';
import { blurFocusedTextControl, formatModelLabel, submitOnEnter } from './shared.js';
import { defaultReasoningLevelLabel, reasoningLevelLabel, REASONING_LEVEL_OPTIONS } from './reasoning-level.js';
import { SkillInvocationField } from './skill-invocation-field.js';
import { useSkillInvocationDraft } from './skill-invocation-draft.js';
import { SnippetPicker, useSnippetPicker } from './snippet-picker.js';
import { FollowUpScheduleFields } from './follow-up-schedule-fields.js';

export function MessageComposer(props: {
  archived: boolean;
  readOnly: boolean;
  compactInput?: boolean;
  environmentId: string;
  environmentBranchOverrides: EnvironmentBranchOverrides;
  environmentOptions: Environment[];
  environmentOptionsLoading: boolean;
  environmentOptionsError: string;
  repository: string;
  inheritedEnvironment: EnvironmentBranchOverrideTarget | null;
  inheritedCodebaseLabel: string;
  inheritedRepository: string;
  repositoryOptions: RepositoryOption[];
  repositoryOptionsLoading: boolean;
  repositoryOptionsError: string;
  branch: string;
  inheritedBranch: string;
  branchOptions: BranchOption[];
  branchOptionsLoading: boolean;
  branchOptionsError: string;
  model: string;
  inheritedModel: string;
  modelChoices: ModelChoice[];
  modelUnavailableReason: string;
  reasoningLevel: ReasoningLevel | '';
  inheritedReasoningLevel: ReasoningLevel | '';
  defaultReasoningLevel: ReasoningLevel | '';
  skills: Skill[];
  skillsEnabled: boolean;
  snippets?: Snippet[];
  snippetsEnabled?: boolean;
  skillsLoading?: boolean;
  skillError?: string;
  onCodebaseChange: (value: string) => void;
  onEnvironmentBranchOverridesChange: (value: EnvironmentBranchOverrides) => void;
  onEnvironmentRepositoryBranchesLoad: (repository: EnvironmentBranchOverrideRepository) => Promise<BranchOption[]>;
  onBranchChange: (value: string) => void;
  onModelChange: (value: string) => void;
  onReasoningLevelChange: (value: ReasoningLevel | '') => void;
  onFocusChange: (focused: boolean) => void;
  onSubmit: (input: { prompt: string; skills: string[]; skillRefs: SkillInvocationRef[] }) => Promise<boolean>;
  onSchedule?: (input: {
    prompt: string;
    skills: string[];
    skillRefs: SkillInvocationRef[];
    schedule: ScheduledFollowUpSchedule;
  }) => Promise<boolean>;
  onSchedulePreview?: (schedule: ScheduledFollowUpSchedule) => Promise<ScheduledFollowUpPreview>;
}) {
  const [prompt, setPrompt] = useState('');
  const [promptResetKey, setPromptResetKey] = useState(0);
  const [branchControlsOpen, setBranchControlsOpen] = useState(false);
  const [composerOptionsOpen, setComposerOptionsOpen] = useState(false);
  const [sendMode, setSendMode] = useState<'now' | 'schedule'>('now');
  const [schedule, setSchedule] = useState<ScheduledFollowUpSchedule | null>(null);
  const [scheduleResetKey, setScheduleResetKey] = useState(0);
  const submitTouchRef = useRef<{ moved: boolean; x: number; y: number } | null>(null);
  const explicitEnvironment =
    props.environmentOptions.find((environment) => environment.id === props.environmentId) ?? null;
  const codebaseValue = props.environmentId
    ? codebaseEnvironmentValue(props.environmentId)
    : props.repository
      ? codebaseRepositoryValue(props.repository)
      : '';
  const inheritedEnvironmentActive = !props.environmentId && !props.repository && Boolean(props.inheritedEnvironment);
  const selectedEnvironment = explicitEnvironment ?? (inheritedEnvironmentActive ? props.inheritedEnvironment : null);
  const branchRepository = selectedEnvironment ? '' : props.repository || props.inheritedRepository;
  const branchControlKey = selectedEnvironment
    ? `environment:${selectedEnvironment.id}`
    : branchRepository
      ? `repository:${branchRepository}`
      : '';
  const branchControlLabel = selectedEnvironment && selectedEnvironment.repositories.length > 1 ? 'Branches' : 'Branch';
  const snippetDraft = useSnippetPicker({
    snippets: props.snippets ?? [],
    enabled: Boolean(props.snippetsEnabled),
    prompt,
    onPromptChange: setPrompt,
  });
  const skillDraft = useSkillInvocationDraft({
    available: props.skills,
    enabled: props.skillsEnabled,
    prompt,
    onPromptChange: setPrompt,
    selectionStart: snippetDraft.selectionStart,
    textareaRef: snippetDraft.textareaRef,
    onSelectionStartChange: snippetDraft.setSelectionStart,
  });

  const canSubmit =
    !props.archived &&
    !props.readOnly &&
    Boolean(prompt.trim() || skillDraft.selectedSkills.length) &&
    !props.modelUnavailableReason;

  useEffect(() => {
    setBranchControlsOpen(false);
  }, [branchControlKey]);

  async function submitPrompt() {
    if (!canSubmit) return;
    const scheduledSubmission =
      sendMode === 'schedule' && schedule && props.onSchedule ? { schedule, submit: props.onSchedule } : null;
    if (sendMode === 'schedule' && !scheduledSubmission) return;
    const submittedPrompt = prompt;
    const submittedSkills = skillDraft.selectedSkills;
    const prepared = skillDraft.prepareSubmission();
    blurFocusedTextControl();
    setPromptResetKey((key) => key + 1);
    setPrompt('');
    skillDraft.clearSelectedSkills();
    const sent = scheduledSubmission
      ? await scheduledSubmission.submit({ ...prepared, schedule: scheduledSubmission.schedule })
      : await props.onSubmit({ ...prepared });
    if (!sent) {
      setPrompt(submittedPrompt);
      skillDraft.restoreSelectedSkills(submittedSkills);
      return;
    }
    setSendMode('now');
    setSchedule(null);
    setScheduleResetKey((key) => key + 1);
  }

  function handleSubmit(event: FormEvent) {
    event.preventDefault();
    void submitPrompt();
  }

  function handlePromptKeyDown(event: Parameters<typeof skillDraft.handlePromptKeyDown>[0]) {
    if (snippetDraft.keyDown(event)) return;
    if (skillDraft.handlePromptKeyDown(event)) return;
    submitOnEnter(event);
  }

  function handleSubmitTouchStart(event: TouchEvent<HTMLButtonElement>) {
    const touch = event.changedTouches[0];
    if (!touch) return;
    submitTouchRef.current = { moved: false, x: touch.clientX, y: touch.clientY };
  }

  function handleSubmitTouchMove(event: TouchEvent<HTMLButtonElement>) {
    const touch = event.changedTouches[0];
    const start = submitTouchRef.current;
    if (!touch || !start) return;
    if (Math.hypot(touch.clientX - start.x, touch.clientY - start.y) > 10) start.moved = true;
  }

  function handleSubmitTouchEnd(event: TouchEvent<HTMLButtonElement>) {
    const start = submitTouchRef.current;
    submitTouchRef.current = null;
    if (!canSubmit || start?.moved) return;
    event.preventDefault();
    void submitPrompt();
  }

  function handleSubmitTouchCancel() {
    submitTouchRef.current = null;
  }

  function handleBlur(event: FocusEvent<HTMLFormElement>) {
    if (!event.currentTarget.contains(event.relatedTarget)) props.onFocusChange(false);
  }

  return (
    <form
      className="shrink-0 bg-background/95 py-3"
      data-thread-composer="true"
      onFocus={() => props.onFocusChange(true)}
      onBlur={handleBlur}
      onSubmit={handleSubmit}
    >
      <Card className="bg-card/90">
        <SkillInvocationField
          controller={skillDraft}
          availableCount={props.skills.length}
          enabled={props.skillsEnabled}
          disabled={props.archived || props.readOnly}
          loading={props.skillsLoading}
          error={props.skillError}
        />
        <SnippetPicker controller={snippetDraft} />
        <Textarea
          ref={snippetDraft.textareaRef}
          key={promptResetKey}
          className={cn(props.compactInput ? 'min-h-12' : 'min-h-28', 'border-0 bg-transparent focus:ring-0')}
          value={prompt}
          onChange={(event) => {
            snippetDraft.setSelectionStart(event.target.selectionStart);
            skillDraft.changePrompt(event.target.value);
          }}
          onSelect={(event) => snippetDraft.setSelectionStart(event.currentTarget.selectionStart)}
          onKeyDown={handlePromptKeyDown}
          placeholder={
            props.archived
              ? 'Restore this archived session before sending new work.'
              : props.readOnly
                ? 'You have read-only access to this session. You can inspect messages, artifacts, and service metadata, but only members and admins can send follow-ups.'
                : 'Ask your deputy to investigate, change code, or follow up...'
          }
          disabled={props.archived || props.readOnly}
        />
        <div className="relative flex flex-wrap items-center gap-2 border-t border-border px-3 py-2 text-xs text-muted-foreground">
          <CodebasePicker
            className="min-w-0 flex-1 md:flex-[2_1_16rem]"
            triggerClassName="h-8 text-xs"
            direction="up"
            value={codebaseValue}
            environments={props.environmentOptions}
            environmentsLoading={props.environmentOptionsLoading}
            environmentsError={props.environmentOptionsError}
            repositories={props.repositoryOptions}
            repositoriesLoading={props.repositoryOptionsLoading}
            repositoriesError={props.repositoryOptionsError}
            onChange={props.onCodebaseChange}
            placeholder={props.inheritedCodebaseLabel || 'Select codebase...'}
            allowEmpty={Boolean(codebaseValue)}
            emptyOptionLabel="Use session codebase"
            disabled={props.archived || props.readOnly}
          />
          <div
            className={cn(
              composerOptionsOpen
                ? 'absolute bottom-full right-2 z-30 mb-1 flex w-72 max-w-[calc(100%-1rem)] flex-col gap-2 rounded-md border border-border bg-card p-2 text-card-foreground shadow-xl'
                : 'hidden',
              'md:contents',
            )}
            role={composerOptionsOpen ? 'dialog' : undefined}
            aria-label={composerOptionsOpen ? 'Message options' : undefined}
            onBlur={(event) => {
              if (!event.currentTarget.contains(event.relatedTarget)) setComposerOptionsOpen(false);
            }}
            onKeyDown={(event) => {
              if (event.key === 'Escape') setComposerOptionsOpen(false);
            }}
          >
            {branchRepository ? (
              <BranchPicker
                className="min-w-0 w-full md:max-w-40 md:flex-[0.8_2_8rem]"
                triggerClassName="h-8 text-xs"
                direction="up"
                value={props.branch}
                branches={props.branchOptions}
                loading={props.branchOptionsLoading}
                error={props.branchOptionsError}
                onChange={props.onBranchChange}
                disabled={props.archived || props.readOnly}
                placeholder={props.inheritedBranch || 'Branch'}
              />
            ) : selectedEnvironment ? (
              <div
                className="w-full shrink-0 md:w-auto"
                onBlur={(event) => {
                  if (!event.currentTarget.contains(event.relatedTarget)) setBranchControlsOpen(false);
                }}
                onKeyDown={(event) => {
                  if (event.key === 'Escape') setBranchControlsOpen(false);
                }}
              >
                <Button
                  className="h-8 w-full px-2 text-xs md:w-auto"
                  type="button"
                  variant={branchControlsOpen ? 'default' : 'secondary'}
                  size="sm"
                  onClick={() => setBranchControlsOpen((open) => !open)}
                  disabled={props.archived || props.readOnly}
                  aria-expanded={branchControlsOpen}
                  aria-haspopup="dialog"
                >
                  {branchControlLabel}
                </Button>
                {branchControlsOpen ? (
                  <div
                    className="absolute bottom-full right-2 z-30 mb-1 w-[28rem] max-w-[calc(100%-1rem)] rounded-md border border-border bg-card p-2 text-card-foreground shadow-xl"
                    role="dialog"
                    aria-label={`${branchControlLabel} for ${selectedEnvironment.name}`}
                  >
                    <EnvironmentBranchOverridesEditor
                      compact
                      environment={selectedEnvironment}
                      value={props.environmentBranchOverrides}
                      direction="down"
                      disabled={props.archived || props.readOnly}
                      onLoadBranches={props.onEnvironmentRepositoryBranchesLoad}
                      onChange={props.onEnvironmentBranchOverridesChange}
                    />
                  </div>
                ) : null}
              </div>
            ) : null}
            <OptionPicker
              className="min-w-0 w-full md:flex-[1_2_9rem]"
              triggerClassName="h-8 text-xs"
              direction="up"
              label="Model"
              value={props.model}
              options={props.modelChoices}
              emptyLabel={props.inheritedModel ? formatModelLabel(props.inheritedModel) : 'Default model'}
              onChange={props.onModelChange}
              disabled={props.archived || props.readOnly || props.modelChoices.length <= 1}
            />
            <OptionPicker
              className="min-w-0 w-full md:flex-[0.8_2_8rem]"
              triggerClassName="h-8 text-xs"
              direction="up"
              label="Reasoning"
              value={props.reasoningLevel}
              options={REASONING_LEVEL_OPTIONS}
              emptyLabel={
                props.inheritedReasoningLevel
                  ? reasoningLevelLabel(props.inheritedReasoningLevel)
                  : defaultReasoningLevelLabel(props.defaultReasoningLevel)
              }
              allowEmpty={Boolean(props.reasoningLevel)}
              onChange={(value) => props.onReasoningLevelChange(value as ReasoningLevel | '')}
              disabled={props.archived || props.readOnly}
            />
          </div>
          {props.modelUnavailableReason ? (
            <p className="basis-full rounded-md border border-warning/50 bg-warning/10 px-2 py-1.5 text-warning-foreground dark:text-warning">
              {props.modelUnavailableReason}
            </p>
          ) : null}
          <div className="ml-auto flex shrink-0 items-center gap-2">
            <Button
              className="h-8 w-8 shrink-0 p-0 md:hidden"
              type="button"
              variant={composerOptionsOpen ? 'secondary' : 'ghost'}
              disabled={props.archived || props.readOnly}
              aria-label={composerOptionsOpen ? 'Close message options' : 'Message options'}
              aria-expanded={composerOptionsOpen}
              aria-haspopup="dialog"
              title={composerOptionsOpen ? 'Close message options' : 'Message options'}
              onClick={() => setComposerOptionsOpen((open) => !open)}
            >
              <SlidersHorizontal className="h-4 w-4" aria-hidden="true" />
            </Button>
            {props.onSchedule && props.onSchedulePreview ? (
              <Button
                className="h-8 w-8 shrink-0 p-0"
                type="button"
                variant={sendMode === 'schedule' ? 'secondary' : 'ghost'}
                disabled={props.archived || props.readOnly}
                aria-label={sendMode === 'schedule' ? 'Close scheduler' : 'Schedule send'}
                aria-pressed={sendMode === 'schedule'}
                title={sendMode === 'schedule' ? 'Close scheduler' : 'Schedule send'}
                onClick={() => setSendMode((mode) => (mode === 'schedule' ? 'now' : 'schedule'))}
              >
                <CalendarClock className="h-4 w-4" aria-hidden="true" />
              </Button>
            ) : null}
            <Button
              className="h-8 w-8 shrink-0 p-0"
              type="submit"
              disabled={!canSubmit || (sendMode === 'schedule' && !schedule)}
              aria-label={sendMode === 'schedule' ? 'Schedule message' : 'Send message'}
              title={sendMode === 'schedule' ? 'Schedule message' : 'Send message'}
              onTouchStart={handleSubmitTouchStart}
              onTouchMove={handleSubmitTouchMove}
              onTouchEnd={handleSubmitTouchEnd}
              onTouchCancel={handleSubmitTouchCancel}
            >
              <SendHorizontal className="h-4 w-4" aria-hidden="true" />
            </Button>
          </div>
          {sendMode === 'schedule' && props.onSchedulePreview ? (
            <FollowUpScheduleFields key={scheduleResetKey} onChange={setSchedule} onPreview={props.onSchedulePreview} />
          ) : null}
        </div>
      </Card>
    </form>
  );
}
