/**
 * ScriptBuilderTab — inline Script Builder controller mounted in the
 * FlowPilot chat region when a fix needs a script drafted.
 *
 * Owns:
 *  - The inline `script_builder_sessions` row (get-or-create via the
 *    POST endpoint with origin='pilot_inline' + ai_session_id).
 *  - AI message state (reuses existing ScriptBuilderChat + ScriptBuilderInput).
 *  - Monaco buffer for the "Write it myself" mode (via ScriptBodyEditor).
 *  - Submit → PATCH /suggested-fixes/:id/script (no applied_at stamp).
 *
 * ScriptBuilderChat stays a pure display component — this controller
 * wires its onSaveScript to the inline submit path.
 *
 * NOTE: CodeModeEditor is NOT used here — it's tightly coupled to
 * treeEditorStore. ScriptBodyEditor is the generic Monaco wrapper.
 *
 * NOTE: `onProgressChange` is expected to be wrapped in useCallback at
 * the call site so the dependency array in the relay effect is stable.
 *
 * Language is hardcoded to 'powershell' for Phase 9 v1. Future: derive
 * from fix metadata or let the engineer pick.
 */
import { useEffect, useState } from 'react'
import { Sparkles, Pencil } from 'lucide-react'
import { cn } from '@/lib/utils'
import { ScriptBuilderChat } from '@/components/script-builder/ScriptBuilderChat'
import { ScriptBuilderInput } from '@/components/script-builder/ScriptBuilderInput'
import { ScriptBodyEditor } from '@/components/script-editor/ScriptBodyEditor'
import { sessionSuggestedFixesApi } from '@/api/sessionSuggestedFixes'
import { scriptBuilderApi } from '@/api'
import type { SessionSuggestedFix } from '@/api/sessionSuggestedFixes'
import type { ScriptBuilderMessage } from '@/types'

export interface ScriptBuilderTabProps {
  fix: SessionSuggestedFix
  pilotSessionId: string
  /** Fires whenever in-progress state changes (for the ChatTabStrip dot).
   *  Wrap in useCallback at the call site to keep the relay effect stable. */
  onProgressChange: (hasProgress: boolean) => void
  /** Fires on successful submit; parent uses this to refresh the fix and hide the tab. */
  onScriptDrafted: (updated: SessionSuggestedFix) => void
}

type Mode = 'ai' | 'editor'

const LANGUAGE = 'powershell'

export function ScriptBuilderTab({
  fix,
  pilotSessionId,
  onProgressChange,
  onScriptDrafted,
}: ScriptBuilderTabProps) {
  const [builderSessionId, setBuilderSessionId] = useState<string | null>(null)
  const [mode, setMode] = useState<Mode>('ai')
  const [messages, setMessages] = useState<ScriptBuilderMessage[]>([])
  const [editorBuffer, setEditorBuffer] = useState<string>(
    () => scaffoldForLanguage(LANGUAGE, fix.description),
  )
  const [aiLoading, setAiLoading] = useState(false)
  const [submitting, setSubmitting] = useState(false)
  const [error, setError] = useState<string | null>(null)
  const [latestScript, setLatestScript] = useState<string | null>(null)

  // Relay in-progress state to parent. Stable as long as onProgressChange
  // is wrapped in useCallback at the call site.
  useEffect(() => {
    const initialScaffold = scaffoldForLanguage(LANGUAGE, fix.description).trim()
    const hasProgress =
      messages.length > 0 ||
      editorBuffer.trim() !== initialScaffold
    onProgressChange(hasProgress)
  }, [messages.length, editorBuffer, fix.description, onProgressChange])

  // Get-or-create the inline session on mount (keyed to pilotSessionId so
  // a new pilot session doesn't reuse a stale builder session).
  useEffect(() => {
    let cancelled = false
    ;(async () => {
      try {
        const s = await scriptBuilderApi.createSession(LANGUAGE, {
          origin: 'pilot_inline',
          aiSessionId: pilotSessionId,
        })
        if (cancelled) return
        setBuilderSessionId(s.id)
        // Resume existing messages if the session was already started
        // (e.g. page refresh). getSession() returns the detail with messages[].
        // listMessages() does NOT exist in the API client — use getSession instead.
        if (s.messages && s.messages.length > 0) {
          setMessages(s.messages)
        }
        if (s.latest_script) setLatestScript(s.latest_script)
      } catch {
        if (!cancelled) setError('Failed to start Script Builder session.')
      }
    })()
    return () => {
      cancelled = true
    }
  }, [pilotSessionId])

  const handleSendMessage = async (content: string) => {
    if (!builderSessionId) return
    setAiLoading(true)
    setError(null)

    // Optimistically add the user message to the list immediately.
    const userMsg: ScriptBuilderMessage = {
      role: 'user',
      content,
      created_at: new Date().toISOString(),
    }
    setMessages((prev) => [...prev, userMsg])

    try {
      // sendMessage returns ScriptBuilderMessageResponse (single assistant reply).
      const reply = await scriptBuilderApi.sendMessage(builderSessionId, content)
      const assistantMsg: ScriptBuilderMessage = {
        role: 'assistant',
        content: reply.content,
        script: reply.script,
        script_filename: reply.script_filename,
        line_count: reply.line_count,
        created_at: reply.timestamp,
      }
      setMessages((prev) => [...prev, assistantMsg])
      if (reply.script) setLatestScript(reply.script)
    } catch {
      // Replace optimistic user message with an error reply so the user sees it.
      const errMsg: ScriptBuilderMessage = {
        role: 'assistant',
        content: 'Something went wrong generating the script. Please try again.',
        created_at: new Date().toISOString(),
      }
      setMessages((prev) => [...prev, errMsg])
    } finally {
      setAiLoading(false)
    }
  }

  const handleSubmit = async (script: string) => {
    if (!script.trim() || submitting) return
    setSubmitting(true)
    setError(null)
    try {
      const updated = await sessionSuggestedFixesApi.patchScript(
        pilotSessionId,
        fix.id,
        script,
      )
      onScriptDrafted(updated)
    } catch {
      setError('Failed to save the drafted script.')
    } finally {
      setSubmitting(false)
    }
  }

  // AI mode: save the latest script produced by the AI.
  const handleAiSave = () => {
    if (latestScript) void handleSubmit(latestScript)
  }

  // onViewScript is required by ScriptBuilderChat — provide a no-op for now
  // (inline preview is a future extension).
  const handleViewScript = (_script: string, _filename: string | null) => {
    // Future: open inline preview panel
  }

  return (
    <div className="flex flex-col h-full bg-bg-page">
      {/* Mode switcher header */}
      <div className="flex items-center justify-between px-4 py-2 border-b border-default shrink-0">
        <div className="text-[12.5px] text-heading font-medium truncate pr-2">
          Script Builder · {fix.title}
        </div>
        <div className="flex items-center gap-2 shrink-0">
          {mode === 'ai' ? (
            <button
              onClick={() => setMode('editor')}
              className="inline-flex items-center gap-1.5 px-2.5 py-1 rounded text-[11.5px] text-muted-foreground border border-default hover:text-primary hover:border-hover transition-colors"
            >
              <Pencil size={11} />
              Write it myself
            </button>
          ) : (
            <button
              onClick={() => setMode('ai')}
              className="inline-flex items-center gap-1.5 px-2.5 py-1 rounded text-[11.5px] text-muted-foreground border border-default hover:text-primary hover:border-hover transition-colors"
            >
              <Sparkles size={11} />
              Back to AI
            </button>
          )}
        </div>
      </div>

      {error && (
        <div className="mx-4 mt-2 text-[11.5px] text-danger bg-danger-dim border border-danger/30 rounded px-2 py-1 shrink-0">
          {error}
        </div>
      )}

      {/* Content area — display:none on inactive mode so state persists */}
      <div className="flex-1 min-h-0">
        {/* AI chat mode */}
        <div className={cn('flex flex-col h-full', mode !== 'ai' && 'hidden')}>
          <div className="flex-1 min-h-0 overflow-y-auto flex flex-col">
            <ScriptBuilderChat
              messages={messages}
              language={LANGUAGE}
              onViewScript={handleViewScript}
              onSaveScript={handleAiSave}
              isLoading={aiLoading}
            />
          </div>
          <div className="shrink-0">
            <ScriptBuilderInput
              onSend={handleSendMessage}
              disabled={aiLoading || !builderSessionId}
              placeholder="Describe the script you need…"
              showSuggestions={messages.length === 0}
            />
          </div>
        </div>

        {/* Editor (Monaco) mode */}
        <div className={cn('flex flex-col h-full', mode !== 'editor' && 'hidden')}>
          <div className="flex-1 min-h-0 p-4">
            <ScriptBodyEditor
              value={editorBuffer}
              onChange={setEditorBuffer}
              disabled={submitting}
            />
          </div>
          <div className="px-4 py-2 border-t border-default flex justify-end shrink-0">
            <button
              onClick={() => void handleSubmit(editorBuffer)}
              disabled={submitting || !editorBuffer.trim()}
              className={cn(
                'px-3.5 py-[7px] rounded text-[12.5px] font-semibold transition-colors',
                'bg-accent text-[#0a0d14] hover:brightness-110 disabled:opacity-50 disabled:cursor-not-allowed',
              )}
            >
              {submitting ? 'Saving…' : 'Submit script'}
            </button>
          </div>
        </div>
      </div>
    </div>
  )
}

function scaffoldForLanguage(language: string, fixDescription: string): string {
  if (language === 'bash' || language === 'python') {
    return `# ${fixDescription}\n\n`
  }
  // PowerShell uses CRLF line endings by convention
  return `# ${fixDescription}\r\n\r\n`
}

export default ScriptBuilderTab
