import { createSlice, createAsyncThunk, PayloadAction } from "@reduxjs/toolkit";
import { commentService, CommentItem } from "@/services/commentService";

/* ── Per-content comment state ────────────────────────────────────────────── */

export interface CommentEntry {
  comments: CommentItem[];
  isLoading: boolean;
  isLoadingMore: boolean;
  isSubmitting: boolean;
  error: string | null;
  currentPage: number;
  hasMore: boolean;
  totalRows: number;
}

interface CommentState {
  /* keyed by "${contentType}-${contentId}" */
  entries: Record<string, CommentEntry>;
}

const initialEntry = (): CommentEntry => ({
  comments: [],
  isLoading: false,
  isLoadingMore: false,
  isSubmitting: false,
  error: null,
  currentPage: 1,
  hasMore: false,
  totalRows: 0,
});

const makeKey = (contentType: number, contentId: string) =>
  `${contentType}-${contentId}`;

/* ── Thunks ───────────────────────────────────────────────────────────────── */

export const fetchComments = createAsyncThunk(
  "comments/fetch",
  async (
    params: { contentType: number; contentId: string; episodeId?: number; pageNo: number },
    { rejectWithValue },
  ) => {
    try {
      const res = await commentService.getComments(params);
      if (res.status !== 200) return rejectWithValue(res.message);
      return {
        key: makeKey(params.contentType, params.contentId),
        comments: res.result.map(commentService.mapComment),
        hasMore: res.more_page,
        currentPage: res.current_page,
        totalRows: res.total_rows,
        pageNo: params.pageNo,
      };
    } catch (err: unknown) {
      return rejectWithValue(err instanceof Error ? err.message : "Failed to load comments");
    }
  },
);

export const submitComment = createAsyncThunk(
  "comments/submit",
  async (
    params: {
      contentType: number;
      contentId: string;
      comment: string;
      commentId?: number;
      episodeId?: number;
      optimisticId: string;
    },
    { rejectWithValue },
  ) => {
    try {
      const res = await commentService.addComment(params);
      if (res.status !== 200) return rejectWithValue(res.message);
      return { key: makeKey(params.contentType, params.contentId), optimisticId: params.optimisticId };
    } catch (err: unknown) {
      return rejectWithValue({
        message: err instanceof Error ? err.message : "Failed to post comment",
        key: makeKey(params.contentType, params.contentId),
        optimisticId: params.optimisticId,
      });
    }
  },
);

export const fetchReplies = createAsyncThunk(
  "comments/fetchReplies",
  async (params: { commentId: string; key: string }, { rejectWithValue }) => {
    try {
      const res = await commentService.getReplies(params.commentId);
      if (res.status !== 200) return rejectWithValue(res.message);
      return { key: params.key, parentId: params.commentId, replies: res.result.map(commentService.mapComment) };
    } catch (err: unknown) {
      return rejectWithValue(err instanceof Error ? err.message : "Failed to load replies");
    }
  },
);

export const deleteComment = createAsyncThunk(
  "comments/delete",
  async (params: { commentId: string; key: string }, { rejectWithValue }) => {
    try {
      const res = await commentService.deleteComment(params.commentId);
      if (res.status !== 200) return rejectWithValue(res.message);
      return { key: params.key, commentId: params.commentId };
    } catch (err: unknown) {
      return rejectWithValue(err instanceof Error ? err.message : "Failed to delete");
    }
  },
);

export const editComment = createAsyncThunk(
  "comments/edit",
  async (params: { commentId: string; comment: string; key: string }, { rejectWithValue }) => {
    try {
      const res = await commentService.editComment({ commentId: params.commentId, comment: params.comment });
      if (res.status !== 200) return rejectWithValue(res.message);
      return { key: params.key, commentId: params.commentId, comment: params.comment };
    } catch (err: unknown) {
      return rejectWithValue(err instanceof Error ? err.message : "Failed to edit");
    }
  },
);

/* ── Slice ────────────────────────────────────────────────────────────────── */

const commentSlice = createSlice({
  name: "comments",
  initialState: { entries: {} } as CommentState,
  reducers: {
    /* Add optimistic comment before API call */
    addOptimistic(state, action: PayloadAction<{ key: string; comment: CommentItem }>) {
      const entry = state.entries[action.payload.key] ?? initialEntry();
      entry.comments = [action.payload.comment, ...entry.comments];
      entry.totalRows = entry.totalRows + 1;
      state.entries[action.payload.key] = entry;
    },
    /* Remove optimistic comment on failure */
    removeOptimistic(state, action: PayloadAction<{ key: string; id: string }>) {
      const entry = state.entries[action.payload.key];
      if (entry) {
        entry.comments = entry.comments.filter((c) => c.id !== action.payload.id);
        entry.totalRows = Math.max(0, entry.totalRows - 1);
      }
    },
    /* Mark optimistic comment as confirmed (remove isOptimistic flag) */
    confirmOptimistic(state, action: PayloadAction<{ key: string; id: string }>) {
      const entry = state.entries[action.payload.key];
      if (entry) {
        const c = entry.comments.find((c) => c.id === action.payload.id);
        if (c) c.isOptimistic = false;
      }
    },
    /* Increment reply count on a comment */
    incrementReplies(state, action: PayloadAction<{ key: string; parentId: string }>) {
      const entry = state.entries[action.payload.key];
      if (entry) {
        const c = entry.comments.find((c) => c.id === action.payload.parentId);
        if (c) c.totalReply = (c.totalReply || 0) + 1;
      }
    },
  },
  extraReducers: (builder) => {
    /* fetchComments */
    builder
      .addCase(fetchComments.pending, (state, action) => {
        const key = makeKey(action.meta.arg.contentType, action.meta.arg.contentId);
        const entry = state.entries[key] ?? initialEntry();
        entry.error = null;
        if (action.meta.arg.pageNo === 1) {
          entry.isLoading = true;
          entry.comments = [];
        } else {
          entry.isLoadingMore = true;
        }
        state.entries[key] = entry;
      })
      .addCase(fetchComments.fulfilled, (state, action) => {
        const entry = state.entries[action.payload.key] ?? initialEntry();
        entry.isLoading = false;
        entry.isLoadingMore = false;
        entry.hasMore = action.payload.hasMore;
        entry.currentPage = action.payload.currentPage;
        entry.totalRows = action.payload.totalRows;
        if (action.payload.pageNo === 1) {
          entry.comments = action.payload.comments;
        } else {
          entry.comments.push(...action.payload.comments);
        }
        state.entries[action.payload.key] = entry;
      })
      .addCase(fetchComments.rejected, (state, action) => {
        const key = makeKey(
          (action.meta.arg as { contentType: number }).contentType,
          (action.meta.arg as { contentId: string }).contentId,
        );
        const entry = state.entries[key] ?? initialEntry();
        entry.isLoading = false;
        entry.isLoadingMore = false;
        entry.error = action.payload as string;
        state.entries[key] = entry;
      });

    /* submitComment */
    builder
      .addCase(submitComment.pending, (state, action) => {
        const key = makeKey(action.meta.arg.contentType, action.meta.arg.contentId);
        const entry = state.entries[key];
        if (entry) entry.isSubmitting = true;
      })
      .addCase(submitComment.fulfilled, (state, action) => {
        const entry = state.entries[action.payload.key];
        if (entry) {
          entry.isSubmitting = false;
          const c = entry.comments.find((c) => c.id === action.payload.optimisticId);
          if (c) c.isOptimistic = false;
        }
      })
      .addCase(submitComment.rejected, (state, action) => {
        const payload = action.payload as { key?: string; optimisticId?: string };
        if (payload?.key) {
          const entry = state.entries[payload.key];
          if (entry) {
            entry.isSubmitting = false;
            // Remove optimistic on failure
            if (payload.optimisticId) {
              entry.comments = entry.comments.filter((c) => c.id !== payload.optimisticId);
              entry.totalRows = Math.max(0, entry.totalRows - 1);
            }
          }
        }
      });

    /* fetchReplies */
    builder.addCase(fetchReplies.fulfilled, (state, action) => {
      const entry = state.entries[action.payload.key];
      if (!entry) return;
      // Remove stale replies for this parent, then add fresh ones
      entry.comments = [
        ...entry.comments.filter((c) => c.parentId !== action.payload.parentId),
        ...action.payload.replies,
      ];
    });

    /* deleteComment */
    builder.addCase(deleteComment.fulfilled, (state, action) => {
      const entry = state.entries[action.payload.key];
      if (entry) {
        entry.comments = entry.comments.filter((c) => c.id !== action.payload.commentId);
        entry.totalRows = Math.max(0, entry.totalRows - 1);
      }
    });

    /* editComment */
    builder.addCase(editComment.fulfilled, (state, action) => {
      const entry = state.entries[action.payload.key];
      if (entry) {
        const c = entry.comments.find((c) => c.id === action.payload.commentId);
        if (c) c.comment = action.payload.comment;
      }
    });
  },
});

export const { addOptimistic, removeOptimistic, confirmOptimistic, incrementReplies } =
  commentSlice.actions;
export default commentSlice.reducer;
