'use client';

import { toast } from '@/components/toast/Toast';
import { components } from '@/lib/gen';

import { RootStore, Substore } from './rootStore';
import { makeAutoObservableSubstore } from './utils';

const PERSONA_PAGE_SIZE = 20;

export type Persona = components['schemas']['PersonaResponseSchema'] & {
  clipIds?: string[];
};

export type PersonaMetadata =
  | components['schemas']['PersonaMetadataResponseSchema']
  | components['schemas']['SimplePersonaSchema'];

export type CreatePersonaParams = {
  user_id: string;
  root_clip_id: string;
  name?: string;
  description?: string;
  image_s3_id?: string | null;
  clips: string[];
  is_public: boolean;
};

export type EditPersonaParams = {
  persona_id: string;
  name?: string;
  description?: string;
  image_s3_id?: string | null;
};

export class PersonaStore implements Substore {
  persona: Persona | null = null;
  personas: Persona[] = [];
  sunoPersonas: Persona[] = [];
  totalUserPersonas: number | null = null;
  totalSunoPersonas: number | null = null;
  currentPageUserPersonas: number = 1;
  currentPageUserPersonasContinuationToken: string | null = null;
  currentPageSunoPersonas: number = 1;
  currentPageLovedPersonas: number = 1;
  lovedPersonas: Persona[] = [];
  totalLovedPersonas: number | null = null;
  followedPersonas: Persona[] = [];
  totalFollowedPersonas: number | null = null;
  currentPageFollowedPersonas: number = 1;

  readonly root: RootStore;
  get apiClient() {
    return this.root.apiClient;
  }
  // DO NOT ADD INIT LOGIC IN constructor. this will run on every page! considering adding any it in the component init logic instead
  constructor(root: RootStore) {
    this.root = root;
    makeAutoObservableSubstore(this);
  }

  createPersona = async (params: CreatePersonaParams) => {
    if (!params.root_clip_id) {
      toast({
        title: 'Error',
        description: 'Root clip ID is required.',
        status: 'error',
        duration: 5000,
        isClosable: true,
      });
      return null;
    }

    const name = params.name || 'Untitled Persona';
    const description = params.description || '';

    try {
      const { data, error, response } = await this.apiClient.POST(
        '/api/persona/create/',
        {
          body: { ...params, name, description },
          headers: {
            'Content-Type': 'application/json',
          },
        }
      );

      if (response.ok && data) {
        this.personas.push(data);
        toast({
          title: 'Success',
          description: 'Persona created successfully.',
          status: 'info',
          duration: 5000,
          isClosable: true,
        });
        return data;
      } else {
        toast({
          title: 'Error',
          description: 'Failed to create persona',
          status: 'error',
          duration: 5000,
          isClosable: true,
        });
        console.error('Error creating persona:', error || 'Unknown error');
        return null;
      }
    } catch (error) {
      toast({
        title: 'Error',
        description: 'An error occurred while creating the persona.',
        status: 'error',
        duration: 5000,
        isClosable: true,
      });
      console.error('Error creating persona:', error);
      return null;
    }
  };

  getPersonasByUser = async (
    page: number = 1,
    continuation_token: string | null = null
  ) => {
    try {
      const { data, error, response } = await this.apiClient.GET(
        '/api/persona/get-personas/',
        {
          params: {
            query: {
              page: page,
              continuation_token: continuation_token,
            },
          },
        }
      );

      if (response.ok && data) {
        // Handle optional fields from PersonasResponseSchema
        const personas = data.personas || [];
        const totalResults = data.total_results || 0;
        const currentPage = data.current_page || 1;
        const continuationToken = data.continuation_token || null;

        if (page > 1) {
          this.personas = [...this.personas, ...personas];
        } else {
          this.personas = personas;
        }
        this.totalUserPersonas = totalResults;
        this.currentPageUserPersonas = currentPage;
        this.currentPageUserPersonasContinuationToken = continuationToken;
        return data;
      } else {
        // toast({
        //   title: 'Error',
        //   description: 'An error occurred while fetching personas.',
        //   status: 'error',
        //   duration: 5000,
        //   isClosable: true,
        // });
        console.error('Error fetching personas:', error || 'Unknown error');
        return null;
      }
    } catch (error) {
      // toast({
      //   title: 'Error',
      //   description: 'An error occurred while fetching personas.',
      //   status: 'error',
      //   duration: 5000,
      //   isClosable: true,
      // });
      console.error('Error fetching personas:', error);
      return null;
    }
  };

  getPersonaById = async (personaId: string) => {
    try {
      const { data, response } = await this.apiClient.GET(
        '/api/persona/get-persona/{persona_id}/',
        {
          params: {
            path: {
              persona_id: personaId,
            },
          },
        }
      );

      if (response.ok && data) {
        this.persona = data;
        return this.persona;
      } else {
        return null;
      }
    } catch (error) {
      return null;
    }
  };

  getPersonaPaginated = async (personaId: string, page: number = 1) => {
    try {
      const { data, response } = await this.apiClient.GET(
        '/api/persona/get-persona-paginated/{persona_id}/',
        {
          params: {
            path: {
              persona_id: personaId,
            },
            query: {
              page,
            },
          },
        }
      );

      if (response.ok && data) {
        if (page === 1) {
          this.persona = data.persona || null;
        }
        return data;
      } else {
        return null;
      }
    } catch (error) {
      return null;
    }
  };

  editPersona = async (params: EditPersonaParams) => {
    try {
      const { data, error, response } = await this.apiClient.PUT(
        `/api/persona/edit-persona/{persona_id}/`,
        {
          params: {
            path: {
              persona_id: params.persona_id,
            },
          },
          body: params,
          headers: {
            'Content-Type': 'application/json',
          },
        }
      );

      if (response.ok && data) {
        const index = this.personas.findIndex(
          (p) => p.id === params.persona_id
        );
        if (index !== -1) {
          this.personas[index] = { ...this.personas[index], ...data };
        }
        if (this.persona && this.persona.id === params.persona_id) {
          this.persona = { ...this.persona, ...data };
        }

        toast({
          title: 'Success',
          description: 'Persona updated successfully.',
          status: 'info',
          duration: 5000,
          isClosable: true,
        });
        return data;
      } else {
        toast({
          title: 'Error',
          description: 'Failed to update persona',
          status: 'error',
          duration: 5000,
          isClosable: true,
        });
        console.error('Error updating persona:', error || 'Unknown error');
        return null;
      }
    } catch (error) {
      toast({
        title: 'Error',
        description: 'An error occurred while updating the persona.',
        status: 'error',
        duration: 5000,
        isClosable: true,
      });
      console.error('Error updating persona:', error);
      return null;
    }
  };

  getSunoPersonas = async (page: number = 1) => {
    try {
      const { data, response } = await this.apiClient.GET(
        '/api/persona/get-suno-personas/',
        {
          params: {
            query: {
              page,
            },
          },
        }
      );

      if (response.ok && data) {
        const personas = data.personas || [];
        if (page > 1) {
          this.sunoPersonas = [
            ...this.sunoPersonas.slice(0, (page - 1) * PERSONA_PAGE_SIZE),
            ...personas,
          ];
        } else {
          this.sunoPersonas = personas;
        }
        this.totalSunoPersonas = data.total_results ?? null;
        this.currentPageSunoPersonas = data.current_page ?? 1;
        return data;
      } else {
        toast({
          title: 'Error',
          description: 'An error occurred while fetching Suno personas.',
          status: 'error',
          duration: 5000,
          isClosable: true,
        });
        console.error('Error fetching Suno personas:', data);
        return null;
      }
    } catch (error) {
      toast({
        title: 'Error',
        description: 'An error occurred while fetching Suno personas.',
        status: 'error',
        duration: 5000,
        isClosable: true,
      });
      console.error('Error fetching Suno personas:', error);
      return null;
    }
  };

  trashPersona = async (personaId: string, undo: boolean = false) => {
    try {
      const { data, response } = await this.apiClient.PUT(
        `/api/persona/trash-persona/{persona_id}/`,
        {
          params: {
            path: {
              persona_id: personaId,
            },
            query: {
              undo: undo,
            },
          },
        }
      );

      if (response.ok && data) {
        const index = this.personas.findIndex((p) => p.id === personaId);
        if (index !== -1) {
          this.personas[index] = { ...this.personas[index], ...data };
        }

        if (this.persona && this.persona.id === personaId) {
          this.persona = { ...this.persona, ...data };
        }

        if (undo) {
          if (!this.personas.some((p) => p.id === personaId)) {
            this.personas.push(data);
          }
        } else {
          this.personas = this.personas.filter((p) => !p.is_trashed);
        }

        return data;
      } else {
        console.error(
          `Error ${undo ? 'restoring' : 'trashing'} persona:`,
          data
        );
        return null;
      }
    } catch (error) {
      console.error(`Error ${undo ? 'restoring' : 'trashing'} persona:`, error);
      return null;
    }
  };

  getTrashedPersonas = async () => {
    try {
      const { data, error, response } = await this.apiClient.GET(
        '/api/persona/get-trashed-personas/'
      );

      if (response.ok && data) {
        return data;
      } else {
        toast({
          title: 'Error',
          description: 'An error occurred while fetching trashed personas.',
          status: 'error',
          duration: 5000,
          isClosable: true,
        });
        console.error(
          'Error fetching trashed personas:',
          error || 'Unknown error'
        );
        return null;
      }
    } catch (error) {
      toast({
        title: 'Error',
        description: 'An error occurred while fetching trashed personas.',
        status: 'error',
        duration: 5000,
        isClosable: true,
      });
      console.error('Error fetching trashed personas:', error);
      return null;
    }
  };

  updatePersona(id: string, updates: Partial<Persona>) {
    const index = this.personas.findIndex((p) => p.id === id);
    if (index !== -1) {
      this.personas[index] = { ...this.personas[index], ...updates };
    }
  }

  setPersonaVisibility = async (personaId: string, isPublic: boolean) => {
    try {
      const { data, error, response } = await this.apiClient.PUT(
        `/api/persona/set_visibility/{persona_id}/`,
        {
          params: {
            path: {
              persona_id: personaId,
            },
            query: {
              is_public: isPublic,
            },
          },
        }
      );

      if (response.ok && data) {
        const index = this.personas.findIndex((p) => p.id === personaId);
        if (index !== -1) {
          this.personas[index] = { ...this.personas[index], ...data };
        }

        if (this.persona && this.persona.id === personaId) {
          this.persona = { ...this.persona, ...data };
        }

        return data;
      } else {
        console.error(
          'Error setting persona visibility:',
          error || 'Unknown error'
        );
        return null;
      }
    } catch (error) {
      console.error('Error setting persona visibility:', error);
      return null;
    }
  };
  toggleLovePersona = async (personaId: string) => {
    try {
      const { data, response } = await this.apiClient.POST(
        `/api/persona/{persona_id}/toggle_love/`,
        {
          params: {
            path: {
              persona_id: personaId,
            },
          },
        }
      );

      if (response.ok && data) {
        const index = this.personas.findIndex((p) => p.id === personaId);
        if (index !== -1) {
          this.personas[index] = {
            ...this.personas[index],
            is_loved: data.loved,
            upvote_count: data.upvote_count,
          };
        }

        if (this.persona && this.persona.id === personaId) {
          this.persona = {
            ...this.persona,
            is_loved: data.loved,
            upvote_count: data.upvote_count,
          };
        }

        const toggledPersona =
          this.personas.find((p) => p.id === personaId) ||
          this.lovedPersonas.find((p) => p.id === personaId);

        if (toggledPersona) {
          toast({
            title: data.loved
              ? `You loved "${toggledPersona.name || 'Persona'}"`
              : `You unloved "${toggledPersona.name || 'Persona'}"`,
            status: 'info',
            duration: 3000,
            isClosable: true,
          });
        }

        return data;
      } else {
        toast({
          title: 'Error',
          description: 'An error occurred while toggling love for the persona.',
          status: 'error',
          duration: 5000,
          isClosable: true,
        });
        console.error('Error toggling love for persona:', data);
        return null;
      }
    } catch (error) {
      toast({
        title: 'Error',
        description: 'An error occurred while toggling love for the persona.',
        status: 'error',
        duration: 5000,
        isClosable: true,
      });
      console.error('Error toggling love for persona:', error);
      return null;
    }
  };

  getLovePersonas = async (page: number = 1) => {
    try {
      const { data, error, response } = await this.apiClient.GET(
        '/api/persona/get-loved-personas/',
        {
          params: {
            query: {
              page,
            },
          },
        }
      );

      if (response.ok && data) {
        // Handle optional fields from PersonasResponseSchema
        const personas = data.personas || [];
        const totalResults = data.total_results || 0;
        const currentPage = data.current_page || 1;

        if (page > 1) {
          this.lovedPersonas = [...this.lovedPersonas, ...personas];
        } else {
          this.lovedPersonas = personas;
        }
        this.totalLovedPersonas = totalResults;
        this.currentPageLovedPersonas = currentPage;
        return data;
      } else {
        if (response.status === 404) {
          this.lovedPersonas = [];
          this.totalLovedPersonas = 0;
          return { personas: [], total_results: 0 };
        }
        console.error(
          'Error fetching loved personas:',
          error || 'Unknown error'
        );
        return null;
      }
    } catch (error) {
      return null;
    }
  };

  toggleFollowPersona = async (personaId: string, isFollowing: boolean) => {
    try {
      const { response } = await this.apiClient.POST(
        `/api/persona/follow/{persona_id}/`,
        {
          params: {
            path: {
              persona_id: personaId,
            },
            query: {
              follow: !isFollowing,
            },
          },
        }
      );

      if (response.ok) {
        return true;
      } else {
        return null;
      }
    } catch (error) {
      return null;
    }
  };

  getFollowedPersonas = async (page: number = 1) => {
    try {
      const { data, error, response } = await this.apiClient.GET(
        '/api/persona/get-followed-personas/',
        {
          params: {
            query: {
              page,
            },
          },
        }
      );

      if (response.ok && data) {
        // Handle optional fields from PersonasResponseSchema
        const personas = data.personas || [];
        const totalResults = data.total_results || 0;
        const currentPage = data.current_page || 1;

        if (page > 1) {
          this.followedPersonas = [...this.followedPersonas, ...personas];
        } else {
          this.followedPersonas = personas;
        }
        this.totalFollowedPersonas = totalResults;
        this.currentPageFollowedPersonas = currentPage;
        return data;
      } else {
        if (response.status === 404) {
          this.followedPersonas = [];
          this.totalFollowedPersonas = 0;
          return { personas: [], total_results: 0 };
        }
        console.error(
          'Error fetching followed personas:',
          error || 'Unknown error'
        );
        return null;
      }
    } catch (error) {
      console.error('Error fetching followed personas:', error);
      return null;
    }
  };
}
