import * as React from "react";
import { api, type ChatWebSocket } from "../api/client";
import type { ChatInputHandle } from "../components/ChatInput";
import type { Message, ProviderConfig } from "../types";

const { useCallback, useRef, useState } = React;

type SetState<T> = React.Dispatch<React.SetStateAction<T>>;

interface UseChatSendParams {
	messages: Message[];
	loading: boolean;
	model: string;
	enableTools: boolean;
	claudeSessionId: string | null;
	streamingContent: string;
	setClaudeSessionId: SetState<string | null>;
	setMessages: SetState<Message[]>;
	setLoading: SetState<boolean>;
	setStreamingContent: SetState<string>;
	setStreamingThinking: SetState<string>;
	setActivityStatus: SetState<string | null>;
	setSideQuestion: SetState<{
		question: string;
		response: string;
		loading: boolean;
	} | null>;
	chatInputRef: React.RefObject<ChatInputHandle | null>;
	wsRef: React.MutableRefObject<ChatWebSocket | null>;
	queuedMessagesRef: React.MutableRefObject<{ id: string; text: string }[]>;
	setQueuedMessages: SetState<{ id: string; text: string }[]>;
	queueIdCounterRef: React.MutableRefObject<number>;
	clearMessages: () => void;
	projectPath: string;
}

export interface UseChatSendResult {
	sendMessage: (text: string) => Promise<void>;
	sendMessageBatch: (texts: string[]) => Promise<void>;
	cancelGeneration: () => Promise<void>;
	handleSaveApiKey: () => Promise<void>;
	clearSession: () => Promise<void>;
	showApiKeyDialog: boolean;
	setShowApiKeyDialog: SetState<boolean>;
	apiKeyInput: string;
	setApiKeyInput: SetState<string>;
}

export function useChatSend({
	messages,
	loading,
	model,
	enableTools,
	claudeSessionId,
	streamingContent,
	setClaudeSessionId,
	setMessages,
	setLoading,
	setStreamingContent,
	setStreamingThinking,
	setActivityStatus,
	setSideQuestion,
	chatInputRef,
	wsRef,
	queuedMessagesRef,
	setQueuedMessages,
	queueIdCounterRef,
	clearMessages,
	projectPath,
}: UseChatSendParams): UseChatSendResult {
	const [showApiKeyDialog, setShowApiKeyDialog] = useState(false);
	const [apiKeyInput, setApiKeyInput] = useState("");
	const pendingMessageRef = useRef<string>("");

	const sendMessage = useCallback(
		async (messageText: string) => {
			if (!messageText.trim()) return;

			if (/^\/reset\s*$/i.test(messageText)) {
				setMessages([]);
				setClaudeSessionId(null);
				setStreamingContent("");
				setStreamingThinking("");
				setActivityStatus(null);
				setMessages([
					{
						role: "assistant",
						content: "Session reset. Starting a fresh conversation.",
					},
				]);
				return;
			}

			const slashMatch = messageText.match(/^\/(\S+)(?:\s+([\s\S]*))?$/);
			if (slashMatch) {
				const cmd = slashMatch[1].toLowerCase();
				const args = (slashMatch[2] ?? "").trim();

				if (cmd !== "btw") {
					const knownCommands = new Set([
						"status",
						"assign",
						"start",
						"show",
						"move",
						"delete",
						"cost",
						"git",
						"overview",
						"rebuild",
						"loc",
						"help",
						"ambient",
						"htop",
						"rmtree",
						"timer",
						"unblock",
						"unreleased",
						"setup",
					]);

					if (knownCommands.has(cmd)) {
						setMessages((prev: Message[]) => [
							...prev,
							{ role: "user", content: messageText },
						]);
						try {
							const result = await api.botCommand(cmd, args);
							setMessages((prev: Message[]) => [
								...prev,
								{ role: "assistant", content: result.response },
							]);
						} catch (e) {
							setMessages((prev: Message[]) => [
								...prev,
								{
									role: "assistant",
									content: `**Error running command:** ${e}`,
								},
							]);
						}
						return;
					}

					setMessages((prev: Message[]) => [
						...prev,
						{ role: "user", content: messageText },
						{
							role: "assistant",
							content: `Unknown command: \`/${cmd}\`. Type \`/help\` to see available commands.`,
						},
					]);
					return;
				}
			}

			const btwMatch = messageText.match(/^\/btw\s+(.+)/s);
			if (btwMatch) {
				const question = btwMatch[1].trim();
				setSideQuestion({ question, response: "", loading: true });

				const isClaudeCode = model === "claude-code-pty";
				const provider = isClaudeCode
					? "claude-code"
					: model.startsWith("claude-")
						? "anthropic"
						: "ollama";
				const config: ProviderConfig = {
					provider,
					model,
					base_url: "http://localhost:11434",
					enable_tools: false,
				};
				wsRef.current?.sendSideQuestion(question, messages, config);
				return;
			}

			if (loading) {
				const newItem = {
					id: String(queueIdCounterRef.current++),
					text: messageText,
				};
				queuedMessagesRef.current = [...queuedMessagesRef.current, newItem];
				setQueuedMessages([...queuedMessagesRef.current]);
				return;
			}

			const isClaudeCode = model === "claude-code-pty";
			if (!isClaudeCode && model.startsWith("claude-")) {
				const hasKey = await api.getAnthropicApiKeyExists();
				if (!hasKey) {
					pendingMessageRef.current = messageText;
					setShowApiKeyDialog(true);
					return;
				}
			}

			const fileRefs = [...messageText.matchAll(/(^|[\s\n])@([^\s@]+)/g)].map(
				(m) => m[2],
			);
			let expandedText = messageText;
			if (fileRefs.length > 0) {
				const expansions = await Promise.allSettled(
					fileRefs.map(async (ref) => {
						const contents = await api.readFile(ref);
						return { ref, contents };
					}),
				);
				for (const result of expansions) {
					if (result.status === "fulfilled") {
						expandedText += `\n\n[File: ${result.value.ref}]\n\`\`\`\n${result.value.contents}\n\`\`\``;
					}
				}
			}

			const userMsg: Message = { role: "user", content: expandedText };
			const newHistory = [...messages, userMsg];

			setMessages(newHistory);
			setLoading(true);
			setStreamingContent("");
			setStreamingThinking("");
			setActivityStatus(null);

			try {
				const provider = isClaudeCode
					? "claude-code"
					: model.startsWith("claude-")
						? "anthropic"
						: "ollama";
				const config: ProviderConfig = {
					provider,
					model,
					base_url: "http://localhost:11434",
					enable_tools: enableTools,
					...(isClaudeCode && claudeSessionId
						? { session_id: claudeSessionId }
						: {}),
				};
				wsRef.current?.sendChat(newHistory, config);
			} catch (e) {
				console.error("Chat error:", e);
				const errorMessage = String(e);
				if (!errorMessage.includes("Chat cancelled by user")) {
					setMessages((prev: Message[]) => [
						...prev,
						{ role: "assistant", content: `**Error:** ${e}` },
					]);
				}
				setLoading(false);
			}
		},
		[
			messages,
			loading,
			model,
			enableTools,
			claudeSessionId,
			setClaudeSessionId,
			setMessages,
			setLoading,
			setStreamingContent,
			setStreamingThinking,
			setActivityStatus,
			setSideQuestion,
			wsRef,
			queuedMessagesRef,
			setQueuedMessages,
			queueIdCounterRef,
		],
	);

	const sendMessageBatch = useCallback(
		async (messageTexts: string[]) => {
			if (messageTexts.length === 0) return;

			const userMsgs: Message[] = messageTexts.map((text) => ({
				role: "user",
				content: text,
			}));
			const newHistory = [...messages, ...userMsgs];

			setMessages(newHistory);
			setLoading(true);
			setStreamingContent("");
			setStreamingThinking("");
			setActivityStatus(null);

			try {
				const isClaudeCode = model === "claude-code-pty";
				const provider = isClaudeCode
					? "claude-code"
					: model.startsWith("claude-")
						? "anthropic"
						: "ollama";
				const config: ProviderConfig = {
					provider,
					model,
					base_url: "http://localhost:11434",
					enable_tools: enableTools,
					...(isClaudeCode && claudeSessionId
						? { session_id: claudeSessionId }
						: {}),
				};
				wsRef.current?.sendChat(newHistory, config);
			} catch (e) {
				console.error("Chat error:", e);
				const errorMessage = String(e);
				if (!errorMessage.includes("Chat cancelled by user")) {
					setMessages((prev: Message[]) => [
						...prev,
						{ role: "assistant", content: `**Error:** ${e}` },
					]);
				}
				setLoading(false);
			}
		},
		[
			messages,
			model,
			enableTools,
			claudeSessionId,
			setMessages,
			setLoading,
			setStreamingContent,
			setStreamingThinking,
			setActivityStatus,
			wsRef,
		],
	);

	const cancelGeneration = useCallback(async () => {
		if (queuedMessagesRef.current.length > 0) {
			const queued = queuedMessagesRef.current
				.map((item) => item.text)
				.join("\n");
			chatInputRef.current?.appendToInput(queued);
		}
		queuedMessagesRef.current = [];
		setQueuedMessages([]);
		try {
			wsRef.current?.cancel();
			await api.cancelChat();

			if (streamingContent) {
				setMessages((prev: Message[]) => [
					...prev,
					{ role: "assistant", content: streamingContent },
				]);
				setStreamingContent("");
			}

			setStreamingThinking("");
			setLoading(false);
			setActivityStatus(null);
		} catch (e) {
			console.error("Failed to cancel chat:", e);
		}
	}, [
		streamingContent,
		queuedMessagesRef,
		setQueuedMessages,
		chatInputRef,
		wsRef,
		setMessages,
		setStreamingContent,
		setStreamingThinking,
		setLoading,
		setActivityStatus,
	]);

	const handleSaveApiKey = useCallback(async () => {
		if (!apiKeyInput.trim()) return;

		try {
			await api.setAnthropicApiKey(apiKeyInput);
			setShowApiKeyDialog(false);
			setApiKeyInput("");

			const pendingMessage = pendingMessageRef.current;
			pendingMessageRef.current = "";

			if (pendingMessage.trim()) {
				sendMessage(pendingMessage);
			}
		} catch (e) {
			console.error("Failed to save API key:", e);
			alert(`Failed to save API key: ${e}`);
		}
	}, [apiKeyInput, sendMessage]);

	const clearSession = useCallback(async () => {
		const confirmed = window.confirm(
			"Are you sure? This will clear all messages and reset the conversation context.",
		);

		if (confirmed) {
			try {
				await api.cancelChat();
				wsRef.current?.cancel();
			} catch (e) {
				console.error("Failed to cancel chat:", e);
			}

			clearMessages();
			setStreamingContent("");
			setStreamingThinking("");
			setLoading(false);
			setActivityStatus(null);
			setClaudeSessionId(null);
			try {
				localStorage.removeItem(`storykit-claude-session-id:${projectPath}`);
			} catch {
				// Ignore — quota or security errors.
			}
		}
	}, [
		wsRef,
		clearMessages,
		setStreamingContent,
		setStreamingThinking,
		setLoading,
		setActivityStatus,
		setClaudeSessionId,
		projectPath,
	]);

	return {
		sendMessage,
		sendMessageBatch,
		cancelGeneration,
		handleSaveApiKey,
		clearSession,
		showApiKeyDialog,
		setShowApiKeyDialog,
		apiKeyInput,
		setApiKeyInput,
	};
}
