import { useEffect, useState } from "preact/hooks";

import { MarkdownContent, stripLeadingTitleHeading } from "./MarkdownContent";
import type {
  CourseCard,
  CourseChapter,
  CourseLesson,
  DiscussionCard,
  DiscussionReply,
  EventCard,
  PostCard,
  PrototypeData,
} from "./types";

function formatTimestamp(value: string): string {
  if (!value) {
    return "Unknown time";
  }

  const date = new Date(value);
  if (Number.isNaN(date.getTime())) {
    return "Unknown time";
  }

  return new Intl.DateTimeFormat("en-US", {
    month: "short",
    day: "numeric",
    hour: "numeric",
    minute: "2-digit",
  }).format(date);
}

function normalizePathname(pathname: string): string {
  if (!pathname || pathname === "/") {
    return "/";
  }

  return pathname.replace(/\/+$/, "") || "/";
}

function parseDiscussionRoute(pathname: string): number | null {
  const match = normalizePathname(pathname).match(/^\/discussions\/(\d+)$/);
  if (!match) {
    return null;
  }

  const issueId = Number(match[1]);
  return Number.isFinite(issueId) ? issueId : null;
}

function parseCourseRoute(pathname: string): { owner: string; repo: string } | null {
  const match = normalizePathname(pathname).match(/^\/courses\/([^/]+)\/([^/]+)$/);
  if (!match) {
    return null;
  }

  return {
    owner: decodeURIComponent(match[1]),
    repo: decodeURIComponent(match[2]),
  };
}

function parseLessonRoute(pathname: string): {
  owner: string;
  repo: string;
  chapter: string;
  lesson: string;
} | null {
  const match = normalizePathname(pathname).match(
    /^\/courses\/([^/]+)\/([^/]+)\/lessons\/([^/]+)\/([^/]+)$/,
  );
  if (!match) {
    return null;
  }

  return {
    owner: decodeURIComponent(match[1]),
    repo: decodeURIComponent(match[2]),
    chapter: decodeURIComponent(match[3]),
    lesson: decodeURIComponent(match[4]),
  };
}

function normalizeRouteKey(value: string): string {
  return decodeURIComponent(value).trim().toLowerCase();
}

function findCourseByRoute(
  courses: CourseCard[],
  route: { owner: string; repo: string },
): CourseCard | undefined {
  const routeOwner = normalizeRouteKey(route.owner);
  const routeRepo = normalizeRouteKey(route.repo);
  const routeFullName = `${routeOwner}/${routeRepo}`;

  return courses.find((course) => {
    const [repoOwner = "", repoName = ""] = course.repo.split("/", 2);
    const courseOwner = normalizeRouteKey(course.owner || repoOwner);
    const courseName = normalizeRouteKey(course.name || repoName);
    const courseFullName = normalizeRouteKey(course.repo);

    return (
      (courseOwner === routeOwner && courseName === routeRepo) ||
      courseFullName === routeFullName ||
      courseName === routeRepo
    );
  });
}

function findLessonByRoute(
  course: CourseCard,
  route: { chapter: string; lesson: string },
): { chapter: CourseChapter; lesson: CourseLesson } | undefined {
  const routeChapter = normalizeRouteKey(route.chapter);
  const routeLesson = normalizeRouteKey(route.lesson);

  for (const chapter of course.outline) {
    if (normalizeRouteKey(chapter.slug) !== routeChapter) {
      continue;
    }

    const lesson = chapter.lessons.find((entry) => normalizeRouteKey(entry.slug) === routeLesson);
    if (lesson) {
      return { chapter, lesson };
    }
  }

  return undefined;
}

function usePathname() {
  const [pathname, setPathname] = useState(() => normalizePathname(window.location.pathname));

  useEffect(() => {
    function handlePopState() {
      setPathname(normalizePathname(window.location.pathname));
    }

    window.addEventListener("popstate", handlePopState);
    return () => {
      window.removeEventListener("popstate", handlePopState);
    };
  }, []);

  function navigate(nextPath: string) {
    const normalized = normalizePathname(nextPath);
    if (normalized === pathname) {
      return;
    }

    window.history.pushState({}, "", normalized);
    window.scrollTo({ top: 0, behavior: "auto" });
    setPathname(normalized);
  }

  return { pathname, navigate };
}

function SectionHeader(props: { title: string }) {
  return (
    <header className="section-header">
      <h2>{props.title}</h2>
    </header>
  );
}

function EmptyState(props: { copy: string }) {
  return <p className="empty-state">{props.copy}</p>;
}

function CourseItem(props: { course: CourseCard; onOpenCourse: (course: CourseCard) => void }) {
  const { course, onOpenCourse } = props;

  return (
    <button
      type="button"
      className="card course-card-button"
      onClick={() => {
        onOpenCourse(course);
      }}
    >
      <h3>{course.title}</h3>
      <p className="muted-copy">{course.summary}</p>
      <p className="meta-line">
        {course.repo} · {course.lessons} lessons · {course.chapters} chapters
      </p>
    </button>
  );
}

function PostItem(props: { post: PostCard }) {
  const { post } = props;

  return (
    <article className="card">
      <h3>{post.title}</h3>
      <p className="muted-copy">{post.summary}</p>
      <p className="meta-line">{post.repo}</p>
    </article>
  );
}

function EventItem(props: { event: EventCard }) {
  const { event } = props;

  return (
    <article className="card">
      <h3>{event.title}</h3>
      <p className="muted-copy">{event.when}</p>
      <p className="meta-line">{event.source}</p>
    </article>
  );
}

function DiscussionPreviewItem(props: {
  discussion: DiscussionCard;
  onOpenDiscussion: (id: number) => void;
}) {
  const { discussion, onOpenDiscussion } = props;

  return (
    <button
      type="button"
      className="discussion-preview-card"
      onClick={() => {
        onOpenDiscussion(discussion.id);
      }}
    >
      <h3>{discussion.title}</h3>
      <p className="meta-line">
        {discussion.repo} · {discussion.author} · {formatTimestamp(discussion.updated_at)} ·{" "}
        {discussion.replies} replies
      </p>
    </button>
  );
}

function DiscussionReplyCard(props: { reply: DiscussionReply }) {
  const { reply } = props;

  return (
    <article className="reply-card">
      <p className="reply-author">{reply.author}</p>
      <p className="meta-line">{formatTimestamp(reply.created_at)}</p>
      <MarkdownContent markdown={reply.body} className="thread-copy" />
    </article>
  );
}

function ComposeBox() {
  return (
    <form
      className="compose-box"
      onSubmit={(event) => {
        event.preventDefault();
      }}
    >
      <textarea className="compose-input" placeholder="Write a reply" />
      <div className="compose-actions">
        <button type="submit" className="compose-button" disabled>
          Post reply
        </button>
      </div>
    </form>
  );
}

function CoursePage(props: {
  course: CourseCard;
  onGoHome: () => void;
  onOpenLesson: (course: CourseCard, chapter: CourseChapter, lesson: CourseLesson) => void;
}) {
  const { course, onGoHome, onOpenLesson } = props;

  return (
    <section className="thread-view">
      <button type="button" className="back-link" onClick={onGoHome}>
        Back to courses
      </button>

      <article className="panel">
        <header className="thread-header">
          <h1>{course.title}</h1>
          <p className="meta-line">
            {course.repo} · {course.lessons} lessons · {course.chapters} chapters
          </p>
        </header>
        <p className="muted-copy">{course.summary}</p>
      </article>

      <article className="panel">
        <header className="subsection-header">
          <h2>Course outline</h2>
        </header>
        {course.outline.length > 0 ? (
          <div className="outline-list">
            {course.outline.map((chapter) => (
              <section key={chapter.slug} className="outline-chapter">
                <h3>{chapter.title}</h3>
                <div className="lesson-list">
                  {chapter.lessons.map((lesson, index) => (
                    <button
                      key={lesson.path}
                      type="button"
                      className="lesson-row lesson-row-button"
                      onClick={() => {
                        onOpenLesson(course, chapter, lesson);
                      }}
                    >
                      <p className="lesson-index">{index + 1 < 10 ? `0${index + 1}` : index + 1}</p>
                      <div>
                        <p className="lesson-title">{lesson.title}</p>
                        {lesson.summary ? <p className="lesson-summary">{lesson.summary}</p> : null}
                        <p className="meta-line">{lesson.file_path || lesson.path}</p>
                      </div>
                    </button>
                  ))}
                </div>
              </section>
            ))}
          </div>
        ) : (
          <EmptyState copy="This course repo has no lesson folders yet." />
        )}
      </article>
    </section>
  );
}

function LessonPage(props: {
  course: CourseCard;
  chapter: CourseChapter;
  lesson: CourseLesson;
  onGoCourse: () => void;
}) {
  const { course, chapter, lesson, onGoCourse } = props;
  const lessonBody = stripLeadingTitleHeading(lesson.body, lesson.title);

  return (
    <section className="thread-view">
      <button type="button" className="back-link" onClick={onGoCourse}>
        Back to course
      </button>

      <article className="panel">
        <header className="thread-header">
          <h1>{lesson.title}</h1>
          <p className="meta-line">
            {course.repo} · {chapter.title}
          </p>
        </header>
        {lesson.summary ? <p className="muted-copy">{lesson.summary}</p> : null}
      </article>

      <article className="panel">
        <header className="subsection-header">
          <h2>Lesson</h2>
        </header>
        {lessonBody ? (
          <MarkdownContent markdown={lessonBody} className="lesson-body" />
        ) : (
          <EmptyState copy="This lesson file is empty or could not be read from Forgejo." />
        )}
      </article>
    </section>
  );
}

function DiscussionPage(props: { discussion: DiscussionCard; onGoHome: () => void }) {
  const { discussion, onGoHome } = props;

  return (
    <section className="thread-view">
      <button type="button" className="back-link" onClick={onGoHome}>
        Back to discussions
      </button>

      <article className="panel">
        <header className="thread-header">
          <h1>{discussion.title}</h1>
          <p className="meta-line">
            {discussion.repo} · Issue #{discussion.number} · {discussion.author} ·{" "}
            {formatTimestamp(discussion.updated_at)}
          </p>
        </header>
        <MarkdownContent markdown={discussion.body} className="thread-copy" />
      </article>

      <article className="panel">
        <header className="subsection-header">
          <h2>Replies</h2>
          <p className="meta-line">{discussion.comments.length}</p>
        </header>
        {discussion.comments.length > 0 ? (
          <div className="reply-list">
            {discussion.comments.map((reply) => (
              <DiscussionReplyCard key={reply.id} reply={reply} />
            ))}
          </div>
        ) : (
          <EmptyState copy="No replies yet." />
        )}
      </article>

      <article className="panel">
        <header className="subsection-header">
          <h2>Reply</h2>
        </header>
        <ComposeBox />
      </article>
    </section>
  );
}

function HomeView(props: {
  data: PrototypeData;
  onOpenCourse: (course: CourseCard) => void;
  onOpenDiscussion: (id: number) => void;
}) {
  const { data, onOpenCourse, onOpenDiscussion } = props;

  return (
    <>
      <section className="page-header">
        <p className="page-kicker">Robot U</p>
        <h1>Courses, projects, and discussions.</h1>
        <p className="muted-copy">
          A single place for lessons, member work, and community conversation.
        </p>
      </section>

      <section className="page-section">
        <SectionHeader title="Courses" />
        {data.featured_courses.length > 0 ? (
          <div className="card-grid">
            {data.featured_courses.map((course) => (
              <CourseItem key={course.repo} course={course} onOpenCourse={onOpenCourse} />
            ))}
          </div>
        ) : (
          <EmptyState copy="No public repos with `/lessons/` were found." />
        )}
      </section>

      <section className="two-column-grid">
        <div className="page-section">
          <SectionHeader title="Posts" />
          {data.recent_posts.length > 0 ? (
            <div className="stack">
              {data.recent_posts.map((post) => (
                <PostItem key={`${post.repo}:${post.title}`} post={post} />
              ))}
            </div>
          ) : (
            <EmptyState copy="No public repos with `/blogs/` were found." />
          )}
        </div>

        <div className="page-section">
          <SectionHeader title="Events" />
          {data.upcoming_events.length > 0 ? (
            <div className="stack">
              {data.upcoming_events.map((event) => (
                <EventItem key={`${event.source}:${event.title}`} event={event} />
              ))}
            </div>
          ) : (
            <EmptyState copy="ICS feeds are not configured yet." />
          )}
        </div>
      </section>

      <section className="page-section">
        <SectionHeader title="Discussions" />
        {data.recent_discussions.length > 0 ? (
          <div className="stack">
            {data.recent_discussions.map((discussion) => (
              <DiscussionPreviewItem
                key={discussion.id}
                discussion={discussion}
                onOpenDiscussion={onOpenDiscussion}
              />
            ))}
          </div>
        ) : (
          <EmptyState copy="No visible Forgejo issues were returned for this account." />
        )}
      </section>
    </>
  );
}

function AppContent(props: {
  data: PrototypeData;
  pathname: string;
  onOpenCourse: (course: CourseCard) => void;
  onOpenLesson: (course: CourseCard, chapter: CourseChapter, lesson: CourseLesson) => void;
  onOpenDiscussion: (id: number) => void;
  onGoHome: () => void;
}) {
  const lessonRoute = parseLessonRoute(props.pathname);
  if (lessonRoute !== null) {
    const selectedCourse = findCourseByRoute(props.data.featured_courses, lessonRoute);
    if (!selectedCourse) {
      return (
        <section className="page-message">
          <h1>Course not found.</h1>
          <button type="button" className="back-link" onClick={props.onGoHome}>
            Back to courses
          </button>
        </section>
      );
    }

    const selectedLesson = findLessonByRoute(selectedCourse, lessonRoute);
    if (!selectedLesson) {
      return (
        <section className="page-message">
          <h1>Lesson not found.</h1>
          <button
            type="button"
            className="back-link"
            onClick={() => {
              props.onOpenCourse(selectedCourse);
            }}
          >
            Back to course
          </button>
        </section>
      );
    }

    return (
      <LessonPage
        course={selectedCourse}
        chapter={selectedLesson.chapter}
        lesson={selectedLesson.lesson}
        onGoCourse={() => {
          props.onOpenCourse(selectedCourse);
        }}
      />
    );
  }

  const courseRoute = parseCourseRoute(props.pathname);
  if (courseRoute !== null) {
    const selectedCourse = findCourseByRoute(props.data.featured_courses, courseRoute);
    if (!selectedCourse) {
      return (
        <section className="page-message">
          <h1>Course not found.</h1>
          <button type="button" className="back-link" onClick={props.onGoHome}>
            Back to courses
          </button>
        </section>
      );
    }

    return (
      <CoursePage
        course={selectedCourse}
        onGoHome={props.onGoHome}
        onOpenLesson={props.onOpenLesson}
      />
    );
  }

  const discussionId = parseDiscussionRoute(props.pathname);
  if (discussionId === null) {
    return (
      <HomeView
        data={props.data}
        onOpenCourse={props.onOpenCourse}
        onOpenDiscussion={props.onOpenDiscussion}
      />
    );
  }

  const selectedDiscussion = props.data.recent_discussions.find(
    (discussion) => discussion.id === discussionId,
  );
  if (!selectedDiscussion) {
    return (
      <section className="page-message">
        <h1>Discussion not found.</h1>
        <button type="button" className="back-link" onClick={props.onGoHome}>
          Back to discussions
        </button>
      </section>
    );
  }

  return <DiscussionPage discussion={selectedDiscussion} onGoHome={props.onGoHome} />;
}

export default function App() {
  const [data, setData] = useState<PrototypeData | null>(null);
  const [error, setError] = useState<string | null>(null);
  const { pathname, navigate } = usePathname();

  useEffect(() => {
    const controller = new AbortController();

    async function loadPrototype() {
      try {
        const response = await fetch("/api/prototype", {
          signal: controller.signal,
        });

        if (!response.ok) {
          throw new Error(`Prototype request failed with ${response.status}`);
        }

        const payload = (await response.json()) as PrototypeData;
        setData(payload);
      } catch (loadError) {
        if (controller.signal.aborted) {
          return;
        }

        const message =
          loadError instanceof Error ? loadError.message : "Unknown prototype loading error";
        setError(message);
      }
    }

    loadPrototype();
    return () => {
      controller.abort();
    };
  }, []);

  function openDiscussion(id: number) {
    navigate(`/discussions/${id}`);
  }

  function openCourse(course: CourseCard) {
    navigate(`/courses/${encodeURIComponent(course.owner)}/${encodeURIComponent(course.name)}`);
  }

  function openLesson(course: CourseCard, chapter: CourseChapter, lesson: CourseLesson) {
    navigate(
      `/courses/${encodeURIComponent(course.owner)}/${encodeURIComponent(course.name)}/lessons/${encodeURIComponent(chapter.slug)}/${encodeURIComponent(lesson.slug)}`,
    );
  }

  function goHome() {
    navigate("/");
  }

  return (
    <main className="app-shell">
      {error ? (
        <section className="page-message">
          <h1>Backend data did not load.</h1>
          <p className="muted-copy">{error}</p>
        </section>
      ) : data ? (
        <AppContent
          data={data}
          pathname={pathname}
          onOpenCourse={openCourse}
          onOpenLesson={openLesson}
          onOpenDiscussion={openDiscussion}
          onGoHome={goHome}
        />
      ) : (
        <section className="page-message">
          <h1>Loading content.</h1>
        </section>
      )}
    </main>
  );
}
