import styled from '@emotion/styled';
import fixStudioProjectState from '@suno/studiokit/projectState/fixStudioProjectState';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { format } from 'date-fns';
import { observer } from 'mobx-react-lite';
import { useEffect, useState } from 'react';
import { useDebounceValue } from 'usehooks-ts';

import { SkeletonBone } from '@/components/layout/Skeleton';
import { useContextSelector } from '@/hooks/useContextSelector';
import {
  BookmarkIcon,
  BookmarkOutlineIcon,
  DuplicateIcon,
  ImageIcon,
  MoreHorizontalIcon,
  PlusIcon,
  SearchIcon,
  StudioIcon,
  TrashIcon,
} from '@/icons';
import { useApiClient } from '@/lib/apiClient';
import logWebUserEvent from '@/logging/logWebUserEvent';

import IntersectionTrigger from '../IntersectionTrigger';
import Button, { ButtonShape, ButtonVariant } from '../button/Button';
import {
  ContextMenuGroup,
  ContextMenuItem,
  ContextMenuTrigger,
  RightClickMenuTrigger,
} from '../contextMenu/ContextMenu';
import Modal from '../modal/Modal';
import SpinnerSVG from '../svg/SpinnerSVG';
import { toast } from '../toast/Toast';
import StudioProjectDetailsModal from './StudioProjectDetailsModal';
import { useStudioProjectsListQuery } from './StudioProjectListView';
import { StudioProjectManagementContext } from './StudioProjectManagementContext';
import getStudioProjectAura from './getStudioProjectAura';
import { getAllUsedClipIds } from './selectors';
import { StudioProjectState } from './types';
import { getBufferKeys } from './uploadedClipCache';

const BackgroundGradient = styled.div`
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 0;
  background:
    linear-gradient(
      190.18deg,
      rgba(0, 0, 0, 0) -0.18%,
      rgba(0, 0, 0, 0.244572) 15.21%,
      rgba(0, 0, 0, 0.6) 34.45%
    ),
    var(--color-background-primary);
`;

const Content = styled.div<{ $isLoading?: boolean }>`
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-rows: auto auto auto 1fr;
  gap: 12px;
  height: 100%;
  min-height: 0;
  filter: ${(props) => (props.$isLoading ? 'blur(4px)' : 'none')};
  transition: filter 0.2s ease-in-out;
`;

const LoadingOverlay = styled.div`
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 100;
  pointer-events: none;
`;

const Title = styled.h3`
  font-size: 20px;
  padding: 0 12px;
  font-weight: 500;
  color: var(--color-foreground-primary);
`;

const TabRow = styled.div`
  display: flex;
  align-items: center;
  border-bottom: 1px solid var(--color-foreground-inactive);
  gap: 12px;
`;

const Tab = styled.button<{ $isActive: boolean }>`
  padding: 12px;
  background: none;
  border: none;
  border-bottom: 1px solid
    ${(props) =>
      props.$isActive ? 'var(--color-foreground-primary)' : 'transparent'};
  color: ${(props) =>
    props.$isActive
      ? 'var(--color-foreground-primary)'
      : 'var(--color-foreground-inactive)'};
  font-size: 16px;
  font-weight: 500;
  cursor: pointer;
  transition:
    color 0.2s ease-in-out,
    border-color 0.2s ease-in-out;
  margin-bottom: -1px;
  &:hover {
    color: var(--color-foreground-primary);
  }
`;

const FilterRow = styled.div`
  display: flex;
  align-items: center;
  gap: 12px;
`;

const SortButtons = styled.div`
  display: flex;
  gap: 8px;
`;

const SearchInputStyled = styled.input`
  flex: 1;
  height: 40px;
  border-radius: 20px;
  padding: 0 16px 0 44px;
  background-color: var(--color-background-secondary);
  color: var(--color-foreground-primary);
  font-size: 14px;
  outline: none;
  &::placeholder {
    color: var(--color-foreground-secondary);
  }
`;

const SearchInputContainer = styled.div`
  position: relative;
  display: flex;
  align-items: center;
  width: 240px;
  svg {
    position: absolute;
    left: 16px;
    color: var(--color-foreground-secondary);
    pointer-events: none;
  }
`;

const ProjectListWrapper = styled.div`
  min-height: 0;
  overflow-y: auto;
`;

const ProjectList = styled.div`
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px 16px;
  overflow-y: auto;
`;

const ProjectItem = styled.div<{ $isArchived?: boolean }>`
  display: flex;
  align-items: center;
  gap: 16px;
  height: 102px;
  padding: 16px;
  border-radius: 16px;
  background-color: var(--color-background-glass-thin);
  cursor: ${(props) => (props.$isArchived ? 'default' : 'pointer')};
  transition: background-color 0.2s ease-in-out;
  &:hover {
    background-color: ${(props) =>
      props.$isArchived
        ? 'var(--color-background-glass-thin)'
        : 'var(--color-background-glass-thick)'};
  }
`;

const ProjectImage = styled.div`
  width: 70px;
  height: 70px;
  border-radius: 8px;
  flex-shrink: 0;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
`;

const NewProjectImagePlaceholder = styled.div`
  width: 70px;
  height: 70px;
  border-radius: 8px;
  flex-shrink: 0;
  background-color: var(--color-background-glass-thin);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--color-foreground-primary);
`;

const ProjectInfo = styled.div`
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 0;
  min-width: 0;
`;

const ProjectTitle = styled.div`
  font-size: 16px;
  font-weight: 500;
  color: var(--color-foreground-primary);
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  word-break: break-word;
`;

const ProjectDate = styled.div`
  font-size: 16px;
  margin-top: -2px;
  color: var(--color-foreground-secondary);
`;

const ProjectActions = styled.div`
  display: flex;
  gap: 4px;
  flex-shrink: 0;
`;

export default observer(function StudioProjectsModal({
  onClose,
}: {
  onClose: () => void;
}) {
  const [localSearchText, setLocalSearchText] = useState('');
  const [debouncedSearchText] = useDebounceValue(localSearchText, 150);
  const [selectedTab, setSelectedTab] = useState(0);
  const [sortOrder, setSortOrder] = useState<'latest' | 'oldest'>('latest');
  const queryClient = useQueryClient();

  // Use archived=true when on "Recently Deleted" tab
  const isArchived = selectedTab === 1;
  const studioProjects = useStudioProjectsListQuery(
    debouncedSearchText,
    isArchived,
    sortOrder
  );

  const loadProject = useContextSelector(
    StudioProjectManagementContext,
    (ctx) => ctx.loadProject
  );
  const createAndLoadNewStudioProject = useContextSelector(
    StudioProjectManagementContext,
    (ctx) => ctx.createAndLoadNewStudioProject
  );
  const createAndSaveNewStudioProject = useContextSelector(
    StudioProjectManagementContext,
    (ctx) => ctx.createAndSaveNewStudioProject
  );
  const apiClient = useApiClient();
  const archiveProjectMutation = useMutation({
    mutationFn: (projectId: string) => {
      return apiClient.POST('/api/studio/project/{project_id}/archive', {
        params: {
          path: {
            project_id: projectId,
          },
        },
      });
    },
    onSuccess: () => {
      // Invalidate both archived and non-archived lists since item moved between them
      queryClient.invalidateQueries({
        queryKey: ['studio-projects'],
        refetchType: 'active', // Only refetch queries that are currently being used
      });
    },
  });

  const unarchiveProjectMutation = useMutation({
    mutationFn: (projectId: string) => {
      return apiClient.POST('/api/studio/project/{project_id}/unarchive', {
        params: {
          path: {
            project_id: projectId,
          },
        },
      });
    },
    onSuccess: () => {
      // Invalidate both archived and non-archived lists since item moved between them
      queryClient.invalidateQueries({
        queryKey: ['studio-projects'],
        refetchType: 'active', // Only refetch queries that are currently being used
      });
    },
  });

  const bookmarkProjectMutation = useMutation({
    mutationFn: ({
      projectId,
      bookmarked,
    }: {
      projectId: string;
      bookmarked: boolean;
    }) => {
      return apiClient.POST('/api/studio/project/{project_id}/bookmark', {
        params: {
          path: {
            project_id: projectId,
          },
        },
        body: {
          bookmarked,
        },
      });
    },
    onMutate: async ({ projectId, bookmarked }) => {
      // Cancel any outgoing refetches
      await queryClient.cancelQueries({ queryKey: ['studio-projects'] });

      // Snapshot the previous value
      const previousData = queryClient.getQueryData([
        'studio-projects',
        debouncedSearchText,
        isArchived,
        sortOrder,
      ]);

      // Optimistically update the cache
      queryClient.setQueryData(
        ['studio-projects', debouncedSearchText, isArchived, sortOrder],
        (old: {
          pages: { projects: { id: string; bookmarked: boolean }[] }[];
        }) => {
          if (!old) return old;
          return {
            ...old,
            pages: old.pages.map(
              (page: { projects: { id: string; bookmarked: boolean }[] }) => ({
                ...page,
                projects: page.projects.map(
                  (project: { id: string; bookmarked: boolean }) =>
                    project.id === projectId
                      ? { ...project, bookmarked }
                      : project
                ),
              })
            ),
          };
        }
      );

      return { previousData };
    },
    onError: (_err, _variables, context) => {
      // Rollback on error
      if (context?.previousData) {
        queryClient.setQueryData(
          ['studio-projects', debouncedSearchText, isArchived, sortOrder],
          context.previousData
        );
      }
    },
    onSettled: () => {
      // Only invalidate the current specific query for consistency check
      queryClient.invalidateQueries({
        queryKey: [
          'studio-projects',
          debouncedSearchText,
          isArchived,
          sortOrder,
        ],
        refetchType: 'active',
      });
    },
  });

  const duplicateProjectMutation = useMutation({
    mutationFn: async ({
      projectId,
      originalTitle,
    }: {
      projectId: string;
      originalTitle: string;
    }) => {
      // Fetch the original project with its state
      const response = await apiClient.GET('/api/studio/project/{project_id}', {
        params: {
          path: {
            project_id: projectId,
          },
        },
      });

      if (!response.data) {
        throw new Error('Failed to fetch project');
      }

      // Fix and validate the state
      const rawProjectState: unknown = response.data.state;
      if (
        typeof rawProjectState !== 'object' ||
        rawProjectState === null ||
        !('timing' in rawProjectState) ||
        !('tracks' in rawProjectState) ||
        !('selection' in rawProjectState)
      ) {
        throw new Error('Invalid project state format');
      }

      const fixedState = fixStudioProjectState(rawProjectState, {
        keepUploads: getBufferKeys(),
      }) as StudioProjectState;

      // Create a new project with "Copy of" prefix
      const newTitle = `Copy of ${originalTitle}`;
      const stateToSave = {
        ...fixedState,
        title: newTitle,
      };
      const newProjectId = await createAndSaveNewStudioProject(
        stateToSave,
        newTitle
      );

      logWebUserEvent({
        actionName: 'CreatedNewStudioProject',
        context: {
          studioProjectId: newProjectId,
          clipIds: getAllUsedClipIds(stateToSave),
          trigger: 'duplicate_project',
        },
      });

      return { newProjectId, newTitle };
    },
    onSuccess: () => {
      toast({
        title: 'Project duplicated',
        description: 'The project has been successfully duplicated.',
        status: 'success',
      });
      // Only refresh the non-archived projects list (duplicates are created there)
      queryClient.invalidateQueries({
        queryKey: ['studio-projects', debouncedSearchText, false, sortOrder],
        refetchType: 'active',
      });
    },
    onError: (error) => {
      console.error('Failed to duplicate project:', error);
      toast({
        title: 'Failed to duplicate project',
        description: 'Please try again.',
        status: 'error',
      });
    },
  });

  const [justArchivedProjects, setJustArchivedProjects] = useState<string[]>(
    []
  );

  // Clear justArchivedProjects when switching tabs to avoid filtering issues
  useEffect(() => {
    setJustArchivedProjects([]);
  }, [selectedTab]);

  const [loadingProject, setLoadingProject] = useState<boolean>(false);
  const [detailsModalProjectId, setDetailsModalProjectId] = useState<
    string | null
  >(null);

  // Close modal on Escape key press
  useEffect(() => {
    const handleEscape = (e: KeyboardEvent) => {
      if (e.key === 'Escape') {
        onClose();
      }
    };

    window.addEventListener('keydown', handleEscape);
    return () => {
      window.removeEventListener('keydown', handleEscape);
    };
  }, [onClose]);

  const handleProjectClick = async (projectId: string) => {
    setLoadingProject(true);
    logWebUserEvent({
      actionName: 'LoadedStudioProject',
      context: {
        studioProjectId: projectId,
        trigger: 'studio_projects_modal',
      },
    });
    await loadProject(projectId, 'studio');
    await new Promise((resolve) => setTimeout(resolve, 1000));
    setLoadingProject(false);
    onClose();
  };

  // Shared menu contents for both context menu and right-click menu
  const renderProjectMenuContents = (
    project: { id: string | null | undefined; title: string | undefined },
    closeMenu?: () => void
  ) => {
    if (isArchived) {
      // Recently Deleted: Only show restore option
      return (
        <ContextMenuGroup>
          <ContextMenuItem
            icon={TrashIcon}
            onClick={() => {
              const projectId = project.id;
              if (!projectId) return;
              unarchiveProjectMutation.mutate(projectId);
              closeMenu?.();
            }}
          >
            Restore Project
          </ContextMenuItem>
        </ContextMenuGroup>
      );
    } else {
      // Active Projects: Show all options
      return (
        <>
          <ContextMenuGroup>
            <ContextMenuItem
              icon={StudioIcon}
              onClick={() => {
                if (project.id) {
                  handleProjectClick(project.id);
                }
                closeMenu?.();
              }}
            >
              Open
            </ContextMenuItem>
            <ContextMenuItem
              icon={DuplicateIcon}
              onClick={(e) => {
                if (project.id && project.title) {
                  duplicateProjectMutation.mutate({
                    projectId: project.id,
                    originalTitle: project.title,
                  });
                }
                closeMenu?.();
                e.stopPropagation();
              }}
            >
              Duplicate
            </ContextMenuItem>
            <ContextMenuItem
              icon={ImageIcon}
              onClick={(e) => {
                if (project.id) {
                  setDetailsModalProjectId(project.id);
                }
                closeMenu?.();
                e.stopPropagation();
              }}
            >
              Project Details
            </ContextMenuItem>
          </ContextMenuGroup>
          <ContextMenuGroup>
            <ContextMenuItem
              className='text-accent-error-on-primary'
              icon={TrashIcon}
              onClick={async (e) => {
                const projectId = project.id;
                if (!projectId) return;
                setJustArchivedProjects((prev) => [...prev, projectId]);
                closeMenu?.();
                e.stopPropagation();
                await archiveProjectMutation.mutateAsync(projectId);
                toast({
                  title: 'Project deleted',
                  status: 'success',
                  duration: 2000,
                  isClosable: true,
                });
              }}
            >
              Delete Project
            </ContextMenuItem>
          </ContextMenuGroup>
        </>
      );
    }
  };

  return (
    <Modal
      onClose={onClose}
      width={null}
      contentWrapperClasses='max-h-[90vh] max-w-[90vw] h-[768px] w-[780px] overflow-hidden border border-border-primary rounded-2xl'
      wrapperClasses='h-full w-full flex flex-col overflow-hidden'
      closeButtonClasses='absolute top-2 right-2 z-100 p-1'
    >
      <BackgroundGradient />
      <Content $isLoading={loadingProject}>
        <Title>My Projects</Title>
        <TabRow>
          <Tab $isActive={selectedTab === 0} onClick={() => setSelectedTab(0)}>
            Projects
          </Tab>
          <Tab $isActive={selectedTab === 1} onClick={() => setSelectedTab(1)}>
            Recently Deleted
          </Tab>
        </TabRow>
        <FilterRow>
          <SortButtons>
            <Button
              variant={
                sortOrder === 'latest'
                  ? ButtonVariant.Standard
                  : ButtonVariant.Secondary
              }
              shape={ButtonShape.Pill}
              onClick={() => setSortOrder('latest')}
            >
              Newest First
            </Button>
            <Button
              variant={
                sortOrder === 'oldest'
                  ? ButtonVariant.Standard
                  : ButtonVariant.Secondary
              }
              shape={ButtonShape.Pill}
              onClick={() => setSortOrder('oldest')}
            >
              Oldest First
            </Button>
          </SortButtons>
          <div className='flex-1' />
          <SearchInputContainer>
            <SearchIcon className='h-5 w-5' />
            <SearchInputStyled
              placeholder='Search for Project'
              value={localSearchText}
              onChange={(e) => {
                setLocalSearchText(e.target.value);
              }}
            />
          </SearchInputContainer>
        </FilterRow>
        <ProjectListWrapper>
          <ProjectList>
            {/* New Project Card */}
            <ProjectItem
              onClick={async () => {
                const project = await createAndLoadNewStudioProject();
                if (project?.id) {
                  logWebUserEvent({
                    actionName: 'CreatedNewStudioProject',
                    context: {
                      studioProjectId: project.id,
                      trigger: 'studio_projects_modal',
                    },
                  });
                }
                onClose();
              }}
            >
              <NewProjectImagePlaceholder>
                <PlusIcon className='h-8 w-8' />
              </NewProjectImagePlaceholder>
              <ProjectInfo>
                <ProjectTitle>New Project</ProjectTitle>
              </ProjectInfo>
            </ProjectItem>

            {/* Project List */}
            {studioProjects.isPending ? (
              <>
                {/* Show 5 skeleton project cards while loading */}
                {Array.from({ length: 5 }).map((_, index) => (
                  <ProjectItem
                    key={`skeleton-${index}`}
                    style={{ pointerEvents: 'none' }}
                  >
                    <SkeletonBone
                      as='div'
                      className='h-[70px] w-[70px] flex-shrink-0 rounded-lg'
                    />
                    <ProjectInfo>
                      <SkeletonBone
                        as='div'
                        className='mb-1 h-5 w-full rounded'
                      />
                      <SkeletonBone as='div' className='h-4 w-24 rounded' />
                    </ProjectInfo>
                  </ProjectItem>
                ))}
              </>
            ) : (
              studioProjects.data?.pages
                .flatMap((page) => page.projects)
                .filter(
                  (
                    project
                  ): project is NonNullable<typeof project> & { id: string } =>
                    !!project && !!project.id
                ) // Filter out null/undefined projects and those without IDs
                .filter((project) => !justArchivedProjects.includes(project.id))
                // Deduplicate by ID in case of race conditions during refetch
                .filter(
                  (project, index, self) =>
                    self.findIndex((p) => p.id === project.id) === index
                )
                .map((project) => (
                  <RightClickMenuTrigger
                    key={project.id}
                    ContentsComponent={() =>
                      renderProjectMenuContents({
                        id: project.id,
                        title: project.title,
                      })
                    }
                  >
                    <ProjectItem
                      $isArchived={isArchived}
                      onClick={async () => {
                        if (!isArchived) {
                          await handleProjectClick(project.id);
                        }
                      }}
                    >
                      <ProjectImage
                        style={{
                          backgroundImage: `url(${
                            project.image_url ||
                            getStudioProjectAura(project.id)
                          })`,
                        }}
                      />
                      <ProjectInfo>
                        <ProjectTitle>{project.title}</ProjectTitle>
                        <ProjectDate>
                          {format(new Date(project.updated_at), 'MMM d, yyyy')}
                        </ProjectDate>
                      </ProjectInfo>
                      <ProjectActions>
                        {!isArchived && (
                          <Button
                            variant={ButtonVariant.Standard}
                            className={
                              project.bookmarked
                                ? 'bg-background-glass-thick'
                                : 'bg-background-glass-thin'
                            }
                            shape={ButtonShape.Pill}
                            onClick={(e) => {
                              e.stopPropagation();
                              bookmarkProjectMutation.mutate({
                                projectId: project.id,
                                bookmarked: !project.bookmarked,
                              });
                            }}
                            icon={
                              project.bookmarked
                                ? BookmarkIcon
                                : BookmarkOutlineIcon
                            }
                          />
                        )}
                        <ContextMenuTrigger
                          ButtonComponent={(props) => (
                            <Button
                              variant={ButtonVariant.Standard}
                              className='bg-background-glass-thin'
                              shape={ButtonShape.Pill}
                              {...props}
                              icon={MoreHorizontalIcon}
                            />
                          )}
                          ContentsComponent={({ onClose: closeMenu }) =>
                            renderProjectMenuContents(
                              {
                                id: project.id,
                                title: project.title,
                              },
                              closeMenu
                            )
                          }
                        />
                      </ProjectActions>
                    </ProjectItem>
                  </RightClickMenuTrigger>
                ))
            )}
            <IntersectionTrigger
              onTrigger={() => studioProjects.fetchNextPage()}
            >
              <div className='h-1 w-1' />
            </IntersectionTrigger>
          </ProjectList>
        </ProjectListWrapper>
      </Content>
      {loadingProject && (
        <LoadingOverlay>
          <SpinnerSVG />
        </LoadingOverlay>
      )}
      {detailsModalProjectId && (
        <StudioProjectDetailsModal
          initialTitle={
            studioProjects.data?.pages
              .flatMap((page) => page.projects)
              .find((p) => p?.id === detailsModalProjectId)?.title || ''
          }
          initialImageUrl={
            studioProjects.data?.pages
              .flatMap((page) => page.projects)
              .find((p) => p?.id === detailsModalProjectId)?.image_url ??
            undefined
          }
          initialNotes={''}
          onClose={() => setDetailsModalProjectId(null)}
          onSave={async (data) => {
            if (!detailsModalProjectId) return;

            // Optimistically update the cache immediately
            queryClient.setQueryData(
              ['studio-projects', debouncedSearchText, isArchived, sortOrder],
              (old: {
                pages: {
                  projects: {
                    id: string;
                    title: string;
                    notes: string;
                    image_url: string;
                    image_s3_id: string;
                  }[];
                }[];
              }) => {
                if (!old) return old;
                return {
                  ...old,
                  pages: old.pages.map(
                    (page: {
                      projects: {
                        id: string;
                        title: string;
                        notes: string;
                        image_url: string;
                        image_s3_id: string;
                      }[];
                    }) => ({
                      ...page,
                      projects: page.projects.map(
                        (project: {
                          id: string;
                          title: string;
                          notes: string;
                          image_url: string;
                          image_s3_id: string;
                        }) =>
                          project.id === detailsModalProjectId
                            ? {
                                ...project,
                                title: data.title,
                                notes: data.notes,
                                image_url: data.imageUrl,
                                image_s3_id: data.imageS3Id,
                              }
                            : project
                      ),
                    })
                  ),
                };
              }
            );

            // Close modal immediately for better UX
            setDetailsModalProjectId(null);

            try {
              const { error, response } = await apiClient.POST(
                '/api/studio/project/{project_id}/metadata',
                {
                  params: {
                    path: { project_id: detailsModalProjectId },
                  },
                  body: {
                    title: data.title,
                    notes: data.notes,
                    image_s3_id: data.imageS3Id,
                  },
                }
              );

              if (error || !response.ok) {
                throw new Error('Failed to update project details');
              }

              // Refetch only the current query to ensure consistency
              queryClient.invalidateQueries({
                queryKey: [
                  'studio-projects',
                  debouncedSearchText,
                  isArchived,
                  sortOrder,
                ],
                refetchType: 'active',
              });
            } catch (err) {
              console.error('Error saving project details:', err);
              alert('Failed to save project details. Please try again.');

              // Refetch on error to restore correct state
              await studioProjects.refetch();
            }
          }}
        />
      )}
    </Modal>
  );
});
