import { useEffect, useState, useCallback, useRef } from "react";
import { useParams, useNavigate } from "react-router";
import { useAuth } from "../contexts/AuthContext";
import { Masthead } from "../components/Masthead";
import { FileDetail, type FocusedElement } from "../components/files/FileDetail";
import { CliInput } from "../components/files/CliInput";
import { ConflictNotification } from "../components/files/ConflictNotification";
import { UpdateNotification } from "../components/files/UpdateNotification";
import { useFiles } from "../hooks/useFiles";
import { useVersionHistory } from "../hooks/useVersionHistory";
import {
  useFileSubscription,
  type FileUpdateEvent,
} from "../hooks/useFileSubscription";
import type { FileDetail as FileDetailType, BodyElement } from "../lib/api";

/**
 * ContractFilePage - Wrapper for viewing files within a contract context
 *
 * This component handles the /contracts/:contractId/files/:fileId route,
 * providing navigation back to the contract and rendering the file detail view.
 */
export default function ContractFilePage() {
  const { id: contractId, fileId } = useParams<{ id: string; fileId: string }>();
  const { isAuthenticated, isAuthConfigured, isLoading: authLoading } = useAuth();
  const navigate = useNavigate();

  // Redirect to login if not authenticated (when auth is configured)
  useEffect(() => {
    if (!authLoading && isAuthConfigured && !isAuthenticated) {
      navigate("/login");
    }
  }, [authLoading, isAuthConfigured, isAuthenticated, navigate]);

  // Show loading while checking auth
  if (authLoading) {
    return (
      <div className="relative z-10 min-h-screen flex flex-col bg-[#0a1628]">
        <Masthead showNav />
        <main className="flex-1 flex items-center justify-center">
          <p className="text-[#7788aa] font-mono text-sm">Loading...</p>
        </main>
      </div>
    );
  }

  // Don't render if not authenticated (will redirect)
  if (isAuthConfigured && !isAuthenticated) {
    return null;
  }

  // Render the file page with contract context
  return <ContractAwareFilesPage contractId={contractId} fileId={fileId} />;
}

// A version of the files page aware of contract context
function ContractAwareFilesPage({
  contractId,
  fileId,
}: {
  contractId?: string;
  fileId?: string;
}) {
  const navigate = useNavigate();
  const { error, conflict, clearConflict, fetchFile, editFile, removeFile } = useFiles();
  const [fileDetail, setFileDetail] = useState<FileDetailType | null>(null);
  const [detailLoading, setDetailLoading] = useState(false);
  const [remoteUpdate, setRemoteUpdate] = useState<FileUpdateEvent | null>(null);
  const [remoteFileData, setRemoteFileData] = useState<FileDetailType | null>(null);
  const [focusedElement, setFocusedElement] = useState<FocusedElement | null>(null);
  const [suggestedPrompt, setSuggestedPrompt] = useState<string | null>(null);
  const pendingUpdateRef = useRef(false);
  const lastSentVersionRef = useRef<number | null>(null);
  const lastSavedVersionRef = useRef<number | null>(null);
  const hasLocalChangesRef = useRef(false);
  const isActivelyEditingRef = useRef(false);
  const currentVersionRef = useRef<number | null>(null);

  // Handle back navigation - go to contract detail instead of /files
  const handleBack = useCallback(() => {
    if (contractId) {
      navigate(`/contracts/${contractId}`);
    } else {
      navigate("/contracts");
    }
  }, [contractId, navigate]);

  const updateHasLocalChanges = useCallback((value: boolean) => {
    hasLocalChangesRef.current = value;
  }, []);

  const updateIsActivelyEditing = useCallback((value: boolean) => {
    isActivelyEditingRef.current = value;
  }, []);

  // Version history
  const {
    versions,
    loading: versionsLoading,
    selectedVersion,
    loadingVersion,
    restoring,
    fetchVersion,
    restoreToVersion,
    clearSelectedVersion,
    fetchVersions,
  } = useVersionHistory({
    fileId: fileId || null,
    currentVersion: fileDetail?.version || 0,
  });

  const handleRestoreVersion = useCallback(
    async (targetVersion: number) => {
      const result = await restoreToVersion(targetVersion);
      if (result) {
        currentVersionRef.current = result.version;
        setFileDetail(result);
        updateHasLocalChanges(false);
        fetchVersions();
      }
    },
    [restoreToVersion, fetchVersions, updateHasLocalChanges]
  );

  // Load file detail when fileId is provided
  useEffect(() => {
    if (fileId) {
      setDetailLoading(true);
      updateHasLocalChanges(false);
      pendingUpdateRef.current = false;
      lastSentVersionRef.current = null;
      lastSavedVersionRef.current = null;
      currentVersionRef.current = null;
      setRemoteUpdate(null);
      setRemoteFileData(null);
      setFocusedElement(null);
      fetchFile(fileId).then((detail) => {
        if (detail) {
          currentVersionRef.current = detail.version;
        }
        setFileDetail(detail);
        setDetailLoading(false);
      });
    } else {
      setFileDetail(null);
      currentVersionRef.current = null;
      updateHasLocalChanges(false);
    }
  }, [fileId, fetchFile, updateHasLocalChanges]);

  // Handle file update events from WebSocket
  const handleFileUpdate = useCallback(
    async (event: FileUpdateEvent) => {
      if (lastSavedVersionRef.current !== null && event.version === lastSavedVersionRef.current) {
        lastSavedVersionRef.current = null;
        return;
      }

      if (pendingUpdateRef.current) {
        if (lastSentVersionRef.current !== null) {
          const expectedNewVersion = lastSentVersionRef.current + 1;
          if (event.version === expectedNewVersion) {
            pendingUpdateRef.current = false;
            lastSentVersionRef.current = null;
            return;
          }
        }
        return;
      }

      if (currentVersionRef.current !== null && event.version === currentVersionRef.current) {
        return;
      }

      if (!hasLocalChangesRef.current && !isActivelyEditingRef.current) {
        const detail = await fetchFile(event.fileId);
        if (detail) {
          currentVersionRef.current = detail.version;
        }
        setFileDetail(detail);
      } else {
        const remoteData = await fetchFile(event.fileId);
        setRemoteFileData(remoteData);
        setRemoteUpdate(event);
      }
    },
    [fetchFile]
  );

  useFileSubscription({
    fileId: fileId || null,
    onUpdate: handleFileUpdate,
  });

  const handleDelete = useCallback(
    async (id: string) => {
      if (confirm("Are you sure you want to delete this file?")) {
        const success = await removeFile(id);
        if (success && fileId === id) {
          handleBack();
        }
      }
    },
    [removeFile, fileId, handleBack]
  );

  const handleSave = useCallback(
    async (id: string, name: string, description: string) => {
      if (!fileDetail) return;
      pendingUpdateRef.current = true;
      lastSentVersionRef.current = fileDetail.version;
      try {
        const result = await editFile(id, { name, description, version: fileDetail.version });
        if (result) {
          lastSavedVersionRef.current = result.version;
          currentVersionRef.current = result.version;
          setFileDetail(result);
          updateHasLocalChanges(false);
        }
      } finally {
        pendingUpdateRef.current = false;
        lastSentVersionRef.current = null;
      }
    },
    [editFile, fileDetail, updateHasLocalChanges]
  );

  const handleBodyUpdate = useCallback(
    (body: BodyElement[], summary: string | null) => {
      if (fileDetail) {
        setFileDetail({
          ...fileDetail,
          body,
          summary,
        });
      }
    },
    [fileDetail]
  );

  const handleBodyElementUpdate = useCallback(
    async (index: number, element: BodyElement) => {
      if (fileDetail && fileId) {
        const newBody = [...fileDetail.body];
        newBody[index] = element;

        setFileDetail({
          ...fileDetail,
          body: newBody,
        });
        updateHasLocalChanges(true);

        pendingUpdateRef.current = true;
        lastSentVersionRef.current = fileDetail.version;
        try {
          const result = await editFile(fileId, { body: newBody, version: fileDetail.version });
          if (result) {
            lastSavedVersionRef.current = result.version;
            currentVersionRef.current = result.version;
            setFileDetail(result);
            updateHasLocalChanges(false);
          }
        } finally {
          pendingUpdateRef.current = false;
          lastSentVersionRef.current = null;
        }
      }
    },
    [fileDetail, fileId, editFile, updateHasLocalChanges]
  );

  const handleBodyReorder = useCallback(
    async (fromIndex: number, toIndex: number) => {
      if (fileDetail && fileId) {
        const newBody = [...fileDetail.body];
        const [movedElement] = newBody.splice(fromIndex, 1);
        newBody.splice(toIndex, 0, movedElement);

        setFileDetail({
          ...fileDetail,
          body: newBody,
        });
        updateHasLocalChanges(true);

        pendingUpdateRef.current = true;
        lastSentVersionRef.current = fileDetail.version;
        try {
          const result = await editFile(fileId, { body: newBody, version: fileDetail.version });
          if (result) {
            lastSavedVersionRef.current = result.version;
            currentVersionRef.current = result.version;
            setFileDetail(result);
            updateHasLocalChanges(false);
          }
        } finally {
          pendingUpdateRef.current = false;
          lastSentVersionRef.current = null;
        }
      }
    },
    [fileDetail, fileId, editFile, updateHasLocalChanges]
  );

  const handleBodyElementDelete = useCallback(
    async (index: number) => {
      if (fileDetail && fileId) {
        const newBody = fileDetail.body.filter((_, i) => i !== index);

        setFileDetail({
          ...fileDetail,
          body: newBody,
        });
        updateHasLocalChanges(true);

        if (focusedElement?.index === index) {
          setFocusedElement(null);
        } else if (focusedElement && focusedElement.index > index) {
          setFocusedElement({
            ...focusedElement,
            index: focusedElement.index - 1,
          });
        }

        pendingUpdateRef.current = true;
        lastSentVersionRef.current = fileDetail.version;
        try {
          const result = await editFile(fileId, { body: newBody, version: fileDetail.version });
          if (result) {
            lastSavedVersionRef.current = result.version;
            currentVersionRef.current = result.version;
            setFileDetail(result);
            updateHasLocalChanges(false);
          }
        } finally {
          pendingUpdateRef.current = false;
          lastSentVersionRef.current = null;
        }
      }
    },
    [fileDetail, fileId, editFile, updateHasLocalChanges, focusedElement]
  );

  const handleBodyElementDuplicate = useCallback(
    async (index: number) => {
      if (fileDetail && fileId) {
        const elementToDuplicate = fileDetail.body[index];
        if (!elementToDuplicate) return;

        const newBody = [...fileDetail.body];
        newBody.splice(index + 1, 0, { ...elementToDuplicate });

        setFileDetail({
          ...fileDetail,
          body: newBody,
        });
        updateHasLocalChanges(true);

        if (focusedElement && focusedElement.index > index) {
          setFocusedElement({
            ...focusedElement,
            index: focusedElement.index + 1,
          });
        }

        pendingUpdateRef.current = true;
        lastSentVersionRef.current = fileDetail.version;
        try {
          const result = await editFile(fileId, { body: newBody, version: fileDetail.version });
          if (result) {
            lastSavedVersionRef.current = result.version;
            currentVersionRef.current = result.version;
            setFileDetail(result);
            updateHasLocalChanges(false);
          }
        } finally {
          pendingUpdateRef.current = false;
          lastSentVersionRef.current = null;
        }
      }
    },
    [fileDetail, fileId, editFile, updateHasLocalChanges, focusedElement]
  );

  const handleFocusElement = useCallback((element: FocusedElement | null) => {
    setFocusedElement(element);
  }, []);

  const handleClearFocus = useCallback(() => {
    setFocusedElement(null);
  }, []);

  const handleConvertElement = useCallback(
    async (index: number, toType: string) => {
      if (!fileDetail || !fileId) return;

      const element = fileDetail.body[index];
      if (!element) return;

      let textContent = "";
      switch (element.type) {
        case "heading":
        case "paragraph":
          textContent = element.text;
          break;
        case "code":
          textContent = element.content;
          break;
        case "list":
          textContent = element.items.join("\n");
          break;
        default:
          return;
      }

      let newElement: BodyElement;
      if (toType === "paragraph") {
        newElement = { type: "paragraph", text: textContent };
      } else if (toType === "list_unordered") {
        const items = textContent.split("\n").filter(line => line.trim());
        newElement = { type: "list", ordered: false, items };
      } else if (toType === "list_ordered") {
        const items = textContent.split("\n").filter(line => line.trim());
        newElement = { type: "list", ordered: true, items };
      } else if (toType === "code") {
        newElement = { type: "code", content: textContent };
      } else if (toType.startsWith("heading_")) {
        const level = parseInt(toType.replace("heading_", ""), 10) as 1 | 2 | 3 | 4 | 5 | 6;
        newElement = { type: "heading", level, text: textContent };
      } else {
        return;
      }

      const newBody = [...fileDetail.body];
      newBody[index] = newElement;

      setFileDetail({ ...fileDetail, body: newBody });
      updateHasLocalChanges(true);

      if (focusedElement?.index === index) {
        setFocusedElement({
          index,
          type: newElement.type,
          preview: textContent.slice(0, 50) + (textContent.length > 50 ? "..." : ""),
        });
      }

      pendingUpdateRef.current = true;
      lastSentVersionRef.current = fileDetail.version;
      try {
        const result = await editFile(fileId, { body: newBody, version: fileDetail.version });
        if (result) {
          lastSavedVersionRef.current = result.version;
          currentVersionRef.current = result.version;
          setFileDetail(result);
          updateHasLocalChanges(false);
        }
      } finally {
        pendingUpdateRef.current = false;
        lastSentVersionRef.current = null;
      }
    },
    [fileDetail, fileId, editFile, updateHasLocalChanges, focusedElement]
  );

  const handleGenerateFromElement = useCallback(
    (index: number, action: string) => {
      if (!fileDetail) return;

      const element = fileDetail.body[index];
      if (!element) return;

      let preview = "";
      switch (element.type) {
        case "heading":
        case "paragraph":
          preview = element.text.slice(0, 50);
          break;
        case "code":
          preview = element.content.slice(0, 50);
          break;
        case "list":
          preview = element.items[0]?.slice(0, 40) || "";
          break;
        default:
          preview = "Element";
      }

      setFocusedElement({
        index,
        type: element.type,
        preview: preview + (preview.length >= 50 ? "..." : ""),
      });

      let prompt = "";
      switch (action) {
        case "elaborate":
          prompt = "Elaborate and expand on this content";
          break;
        case "summarize":
          prompt = "Summarize this content";
          break;
        case "extract_actions":
          prompt = "Extract action items from this content";
          break;
      }
      setSuggestedPrompt(prompt);
    },
    [fileDetail]
  );

  // Conflict resolution handlers
  const handleConflictReload = useCallback(async () => {
    if (fileId) {
      clearConflict();
      const detail = await fetchFile(fileId);
      if (detail) {
        currentVersionRef.current = detail.version;
      }
      setFileDetail(detail);
      updateHasLocalChanges(false);
    }
  }, [fileId, clearConflict, fetchFile, updateHasLocalChanges]);

  const handleConflictForceOverwrite = useCallback(async () => {
    if (fileId && fileDetail) {
      clearConflict();
      const latest = await fetchFile(fileId);
      if (latest) {
        pendingUpdateRef.current = true;
        lastSentVersionRef.current = latest.version;
        try {
          const result = await editFile(fileId, { body: fileDetail.body, version: latest.version });
          if (result) {
            lastSavedVersionRef.current = result.version;
            currentVersionRef.current = result.version;
            setFileDetail(result);
            updateHasLocalChanges(false);
          }
        } finally {
          pendingUpdateRef.current = false;
          lastSentVersionRef.current = null;
        }
      }
    }
  }, [fileId, fileDetail, clearConflict, fetchFile, editFile, updateHasLocalChanges]);

  const handleRemoteUpdateRefresh = useCallback(async () => {
    if (fileId) {
      const detail = await fetchFile(fileId);
      if (detail) {
        currentVersionRef.current = detail.version;
      }
      setFileDetail(detail);
      setRemoteUpdate(null);
      setRemoteFileData(null);
      updateHasLocalChanges(false);
    }
  }, [fileId, fetchFile, updateHasLocalChanges]);

  const handleRemoteUpdateDismiss = useCallback(() => {
    setRemoteUpdate(null);
    setRemoteFileData(null);
  }, []);

  return (
    <div className="relative z-10 h-screen flex flex-col overflow-hidden">
      <Masthead showTicker={false} showNav />

      <main className="flex-1 p-4 md:p-6 min-h-0 overflow-hidden flex flex-col">
        {error && (
          <div className="mb-4 p-3 border border-red-400/50 bg-red-400/10 text-red-400 font-mono text-sm shrink-0">
            {error}
          </div>
        )}

        {fileId && fileDetail ? (
          <div className="flex-1 flex flex-col min-h-0 overflow-hidden">
            <div className="flex-1 min-h-0 overflow-hidden">
              <FileDetail
                file={fileDetail}
                loading={detailLoading}
                onBack={handleBack}
                onSave={handleSave}
                onDelete={handleDelete}
                onBodyElementUpdate={handleBodyElementUpdate}
                onBodyReorder={handleBodyReorder}
                onBodyElementDelete={handleBodyElementDelete}
                onBodyElementDuplicate={handleBodyElementDuplicate}
                onConvertElement={handleConvertElement}
                onGenerateFromElement={handleGenerateFromElement}
                onEditingChange={updateIsActivelyEditing}
                hasPendingRemoteUpdate={!!remoteUpdate}
                onOverwrite={handleRemoteUpdateDismiss}
                focusedElement={focusedElement}
                onFocusElement={handleFocusElement}
                versions={versions}
                versionsLoading={versionsLoading}
                selectedVersion={selectedVersion}
                loadingVersion={loadingVersion}
                restoring={restoring}
                onSelectVersion={fetchVersion}
                onRestoreVersion={handleRestoreVersion}
                onClearVersionSelection={clearSelectedVersion}
              />
            </div>
            <div className="shrink-0">
              <CliInput
                fileId={fileId}
                onUpdate={handleBodyUpdate}
                focusedElement={focusedElement}
                onClearFocus={handleClearFocus}
                suggestedPrompt={suggestedPrompt}
                onClearSuggestedPrompt={() => setSuggestedPrompt(null)}
              />
            </div>
          </div>
        ) : fileId && detailLoading ? (
          <div className="panel h-full flex items-center justify-center">
            <div className="font-mono text-[#9bc3ff] text-sm">Loading...</div>
          </div>
        ) : (
          <div className="panel h-full flex items-center justify-center">
            <div className="text-center">
              <p className="font-mono text-sm text-[#555] mb-4">
                File not found
              </p>
              <button
                onClick={handleBack}
                className="px-4 py-2 font-mono text-xs text-[#75aafc] hover:text-[#9bc3ff] transition-colors"
              >
                &larr; Back to Contract
              </button>
            </div>
          </div>
        )}
      </main>

      {/* Conflict notification */}
      {conflict?.hasConflict && (
        <ConflictNotification
          onReload={handleConflictReload}
          onForceOverwrite={handleConflictForceOverwrite}
          onDismiss={clearConflict}
        />
      )}

      {/* Remote update notification */}
      {remoteUpdate && (
        <UpdateNotification
          updatedBy={remoteUpdate.updatedBy}
          localBody={fileDetail?.body || []}
          remoteBody={remoteFileData?.body || []}
          onRefresh={handleRemoteUpdateRefresh}
          onDismiss={handleRemoteUpdateDismiss}
        />
      )}
    </div>
  );
}
