"use client";

import { useState, useEffect } from "react";
import { useRouter } from "next/navigation";
import { deleteDraft } from "@/data/drafts/actions";
import { DraftSaver } from "@/data/drafts/DraftSaver";
import { publishDraft } from "@/data/actions";
import { useDraftLock } from "./useDraftLock";
import { EditModeProvider } from "@/app/at/(trail)/[handle]/trail/[rkey]/EditModeContext";
import { TrailOverview } from "@/app/at/(trail)/[handle]/trail/[rkey]/TrailOverview";
import { TrailColorPicker } from "@/app/TrailColorPicker";
import { TrailWalk } from "@/app/at/(trail)/[handle]/trail/[rkey]/TrailWalk";
import type { DraftDetail } from "@/data/drafts/queries";
import type { ExternalEmbed } from "@/data/queries";
import { generateTid } from "@/data/tid";
import { useAuthAction } from "@/auth/useAuthAction";
import "./DraftEditor.css";

const EMPTY_WALKERS_PROMISE = Promise.resolve(
  [] as Array<{
    user: { did: string; handle: string; avatar?: string };
    isYou: boolean;
    key: string;
  }>,
);
const EMPTY_COMPLETIONS_PROMISE = Promise.resolve(
  [] as Array<{
    user: { did: string; handle: string; avatar?: string };
    timestamp: Date;
    completionUri: string;
    isYou: boolean;
  }>,
);
const EMPTY_STOP_WALKERS_PROMISE = Promise.resolve(
  [] as Array<{ did: string; handle: string; avatar?: string }>,
);

type Props = {
  rkey: string;
  initialDraft: DraftDetail;
  initialEmbeds?: Array<[string, Promise<React.ReactElement>]>;
};

export function DraftEditor({ rkey, initialDraft, initialEmbeds }: Props) {
  const { hasConflict, canSave } = useDraftLock(rkey);
  return (
    <DraftEditorContent
      rkey={rkey}
      initialDraft={initialDraft}
      hasTabConflict={hasConflict}
      canSave={canSave}
      initialEmbeds={initialEmbeds}
    />
  );
}

type ContentProps = Props & { hasTabConflict: boolean; canSave: boolean };

function DraftEditorContent({
  rkey,
  initialDraft,
  hasTabConflict,
  canSave,
  initialEmbeds,
}: ContentProps) {
  const router = useRouter();
  const requireAuth = useAuthAction();

  const [hasConflict, setHasConflict] = useState(false);

  const hasStopWithContent = initialDraft.stops.some(
    (stop: any) =>
      (stop.content && stop.content.trim().length > 0) ||
      (stop.buttonText && stop.buttonText.trim().length > 0) ||
      stop.external,
  );

  const [stage, setStage] = useState<"overview" | "walking">(
    hasStopWithContent ? "walking" : "overview",
  );

  const [localDraft, setLocalDraft] = useState<{
    title: string;
    description: string;
    stops: Array<{
      tid: string;
      title: string;
      content: string;
      buttonText?: string;
      external?: ExternalEmbed;
    }>;
    accentColor: string;
    backgroundColor: string;
  }>({
    title: initialDraft.title,
    description: initialDraft.description,
    stops: initialDraft.stops,
    accentColor: initialDraft.accentColor,
    backgroundColor: initialDraft.backgroundColor,
  });

  const [saver] = useState(
    () => new DraftSaver(rkey, initialDraft.version, localDraft, () => setHasConflict(true)),
  );

  useEffect(() => {
    if (canSave && !hasConflict) saver.save(localDraft);
  }, [localDraft, canSave, hasConflict, saver]);

  const handleOverwrite = async () => {
    if (!confirm("overwrite changes from another device?")) return;
    await saver.saveNow(localDraft);
    setHasConflict(false);
  };

  const handleDiscard = () => {
    if (!confirm("discard your changes and reload?")) return;
    window.location.reload();
  };

  const updateTrail = (updates: Partial<typeof localDraft>) => {
    setLocalDraft((prev) => ({ ...prev, ...updates }));
  };

  const updateStop = (stopId: string, updates: Partial<(typeof localDraft.stops)[0]>) => {
    setLocalDraft((prev) => ({
      ...prev,
      stops: prev.stops.map((stop) => (stop.tid === stopId ? { ...stop, ...updates } : stop)),
    }));
    if (inlineErrors[stopId]) {
      setInlineErrors((prev) => {
        const next = { ...prev };
        delete next[stopId];
        return next;
      });
    }
  };

  const addStop = (afterIndex?: number) => {
    const newStop = {
      tid: generateTid(),
      title: "",
      content: "",
    };
    setLocalDraft((prev) => {
      let newStops;
      if (afterIndex !== undefined) {
        newStops = [
          ...prev.stops.slice(0, afterIndex + 1),
          newStop,
          ...prev.stops.slice(afterIndex + 1),
        ];
      } else {
        newStops = [...prev.stops, newStop];
      }
      return { ...prev, stops: newStops };
    });
  };

  const deleteStop = (stopId: string) => {
    setLocalDraft((prev) => {
      if (prev.stops.length === 1) return prev;

      return {
        ...prev,
        stops: prev.stops.filter((stop) => stop.tid !== stopId),
      };
    });
  };

  const moveStop = (stopId: string, direction: "up" | "down") => {
    setLocalDraft((prev) => {
      const index = prev.stops.findIndex((s) => s.tid === stopId);
      if (index === -1) return prev;

      const newStops = [...prev.stops];
      if (direction === "up" && index > 0) {
        [newStops[index - 1], newStops[index]] = [newStops[index], newStops[index - 1]];
      } else if (direction === "down" && index < newStops.length - 1) {
        [newStops[index], newStops[index + 1]] = [newStops[index + 1], newStops[index]];
      } else {
        return prev;
      }

      return { ...prev, stops: newStops };
    });
  };

  const handleDelete = async () => {
    if (confirm("delete this draft? it will be gone forever")) {
      await deleteDraft(rkey);
      router.push("/drafts");
    }
  };

  const handleStartWalking = () => {
    setStage("walking");
    window.scrollTo(0, 0);
  };

  const [isPublishing, setIsPublishing] = useState(false);
  const [publishError, setPublishError] = useState<string[] | null>(null);
  const [inlineErrors, setInlineErrors] = useState<Record<string, string>>({});

  const handlePublish = async () => {
    requireAuth();
    setPublishError(null);
    setInlineErrors({});
    setIsPublishing(true);

    try {
      await saver.saveNow(localDraft);

      const result = await publishDraft({
        title: localDraft.title,
        description: localDraft.description,
        stops: localDraft.stops,
        accentColor: localDraft.accentColor,
        backgroundColor: localDraft.backgroundColor,
      });

      if (!result.success) {
        setPublishError(result.errors);
        setInlineErrors(result.inlineErrors);
        setIsPublishing(false);
        return;
      }

      await deleteDraft(rkey);
      router.replace(`/@${result.handle}/trail/${result.rkey}`);
    } catch (error: unknown) {
      const message = error instanceof Error ? error.message : "something went wrong";
      setPublishError([message]);
      setIsPublishing(false);
    }
  };

  const trailDetailData = {
    header: {
      uri: `at://did:plc:fake/app.sidetrail.draft/${rkey}`,
      cid: "draft",
      rkey,
      title: localDraft.title,
      description: localDraft.description,
      accentColor: localDraft.accentColor,
      backgroundColor: localDraft.backgroundColor,
      creator: {
        did: "did:plc:draft",
        handle: "you",
        avatar: undefined,
      },
      stopsCount: localDraft.stops.length,
      createdAt: new Date(),
    },
    stops: localDraft.stops.map((stop) => ({
      tid: stop.tid,
      title: stop.title,
      content: stop.content,
      buttonText: stop.buttonText,
      external: stop.external,
      walkersHere: EMPTY_STOP_WALKERS_PROMISE,
    })),
    yourWalk: null,
    walkers: EMPTY_WALKERS_PROMISE,
    completions: EMPTY_COMPLETIONS_PROMISE,
  };

  return (
    <EditModeProvider
      value={{
        trail: {
          ...localDraft,
          createdAt: initialDraft.createdAt.toISOString(),
          updatedAt: new Date().toISOString(),
        },
        updateTrail,
        updateStop,
        addStop,
        deleteStop,
        moveStop,
        inlineErrors,
      }}
    >
      <div
        style={
          {
            "--accent-color": localDraft.accentColor,
            "--bg-color": localDraft.backgroundColor,
          } as React.CSSProperties
        }
      >
        <div
          inert={hasTabConflict ? true : undefined}
          className={`DraftEditor-content${hasTabConflict ? " is-blocked" : ""}`}
        >
          {hasConflict && (
            <div className="DraftEditor-conflict">
              <p>autosave disabled — this draft was edited on another device</p>
              <div className="DraftEditor-conflictActions">
                <button onClick={handleOverwrite}>save and overwrite</button>
                <button onClick={handleDiscard}>reload without saving</button>
              </div>
            </div>
          )}

          <TrailColorPicker
            currentAccentColor={localDraft.accentColor}
            onColorChange={(accent, background) => {
              updateTrail({
                accentColor: accent,
                backgroundColor: background,
              });
            }}
          />

          {stage === "overview" && (
            <TrailOverview
              trail={trailDetailData}
              onModeChange={handleStartWalking}
              canEdit={true}
              onDelete={handleDelete}
            />
          )}

          {stage === "walking" && (
            <TrailWalk
              trail={trailDetailData}
              cleanHandle="drafts"
              rkey={rkey}
              onModeChange={() => setStage("overview")}
              isEditMode={true}
              onPublish={handlePublish}
              publishError={publishError}
              isPublishing={isPublishing}
              initialEmbeds={initialEmbeds}
            />
          )}
        </div>

        {hasTabConflict && (
          <div className="DraftEditor-blockedOverlay">
            <div className="DraftEditor-blockedMessage">
              <p>this draft is open in another tab</p>
              <p>close that tab and refresh to edit here</p>
            </div>
          </div>
        )}
      </div>
    </EditModeProvider>
  );
}
