import camelcaseKeys from 'camelcase-keys';
import CompanyAPI from 'dashboard/api/companies';
import { createStore } from 'dashboard/store/storeFactory';
import { throwErrorMessage } from 'dashboard/store/utils/api';
import snakecaseKeys from 'snakecase-keys';

const createInitialUIFlags = () => ({
  fetchingList: false,
  fetchingItem: false,
  updatingItem: false,
  creatingItem: false,
  deletingItem: false,
  deletingAvatar: false,
  deletingCustomAttributes: false,
  fetchingContacts: false,
  fetchingConversations: false,
  fetchingNotes: false,
  searchingContacts: false,
  creatingContact: false,
  removingContact: false,
});

const camelizeCompany = data =>
  camelcaseKeys(data || {}, { deep: true, stopPaths: ['custom_attributes'] });

const camelizeContact = data =>
  camelcaseKeys(data || {}, {
    deep: true,
    stopPaths: ['custom_attributes', 'additional_attributes'],
  });

const normalizeMeta = meta => ({
  ...camelcaseKeys(meta || {}),
  totalCount: Number(meta?.total_count || meta?.totalCount || meta?.count || 0),
  page: Number(meta?.page || meta?.current_page || 1),
});

const appendFormData = (formData, key, value) => {
  if (value === undefined || value == null || value === '') return;
  if (
    value instanceof File ||
    value instanceof Blob ||
    typeof value !== 'object'
  ) {
    formData.append(key, value);
    return;
  }
  Object.entries(value).forEach(([k, v]) =>
    appendFormData(formData, `${key}[${k}]`, v)
  );
};

const buildCompanyRequestPayload = ({ avatar, customAttributes, ...rest }) => {
  const payload = {
    ...snakecaseKeys(rest, { deep: true }),
    ...(customAttributes && { custom_attributes: customAttributes }),
    ...(avatar && { avatar }),
  };
  if (!avatar) return { company: payload };

  const formData = new FormData();
  Object.entries(payload).forEach(([k, v]) =>
    appendFormData(formData, `company[${k}]`, v)
  );
  return formData;
};

export const useCompaniesStore = createStore({
  name: 'companies',
  type: 'pinia',
  API: CompanyAPI,
  state: () => ({
    activeCompanyId: null,
    companyContacts: [],
    companyContactsMeta: {},
    companyConversations: [],
    companyNotes: [],
    contactSearchResults: [],
    contactSearchMeta: {},
    activeContactSearchQuery: '',
  }),

  getters: {
    getCompaniesList: state => state.records,
  },

  actions: () => ({
    setMeta(meta) {
      this.meta = normalizeMeta(meta);
    },

    setActiveCompanyId(companyId) {
      this.activeCompanyId = Number(companyId);
    },

    ensureActiveCompanyContext(companyId) {
      if (this.activeCompanyId === null) {
        this.setActiveCompanyId(companyId);
      }
    },

    upsertCompanyRecord(record) {
      const index = this.records.findIndex(r => r.id === record.id);
      if (index === -1) this.records.push(record);
      else this.records[index] = record;
    },

    updateCompanyContactsCount(companyId, contactsCount) {
      const company = this.getRecord(companyId);
      if (!company.id) return;
      this.upsertCompanyRecord({ ...company, contactsCount });
    },

    clearContactSearchResults() {
      this.contactSearchResults = [];
      this.contactSearchMeta = {};
      this.activeContactSearchQuery = '';
      this.contactSearchRequestToken =
        (this.contactSearchRequestToken || 0) + 1;
    },

    async get({ page = 1, sort = 'name' } = {}) {
      this.setUIFlag({ fetchingList: true });
      try {
        const {
          data: { payload, meta },
        } = await CompanyAPI.get({ page, sort });
        this.records = camelizeCompany(payload);
        this.setMeta(meta);
        return this.records;
      } catch (error) {
        return throwErrorMessage(error);
      } finally {
        this.setUIFlag({ fetchingList: false });
      }
    },

    async show(id) {
      this.setUIFlag({ fetchingItem: true });
      this.setActiveCompanyId(id);
      const activeCompanyId = Number(id);
      const requestToken = (this.companyDetailRequestToken || 0) + 1;
      this.companyDetailRequestToken = requestToken;
      try {
        const {
          data: { payload },
        } = await CompanyAPI.show(id);
        const company = camelizeCompany(payload);

        if (
          this.companyDetailRequestToken !== requestToken ||
          this.activeCompanyId !== activeCompanyId
        ) {
          return company;
        }

        this.upsertCompanyRecord(company);
        this.setActiveCompanyId(company.id);
        return company;
      } catch (error) {
        return throwErrorMessage(error);
      } finally {
        if (this.companyDetailRequestToken === requestToken) {
          this.setUIFlag({ fetchingItem: false });
        }
      }
    },

    async update({ id, ...companyAttrs }) {
      this.setUIFlag({ updatingItem: true });
      try {
        const {
          data: { payload },
        } = await CompanyAPI.update(
          id,
          buildCompanyRequestPayload(companyAttrs)
        );
        const company = camelizeCompany(payload);
        this.upsertCompanyRecord(company);
        return company;
      } catch (error) {
        return throwErrorMessage(error);
      } finally {
        this.setUIFlag({ updatingItem: false });
      }
    },

    async create(companyAttrs) {
      this.setUIFlag({ creatingItem: true });
      try {
        const {
          data: { payload },
        } = await CompanyAPI.create(buildCompanyRequestPayload(companyAttrs));
        const company = camelizeCompany(payload);
        this.upsertCompanyRecord(company);
        return company;
      } catch (error) {
        return throwErrorMessage(error);
      } finally {
        this.setUIFlag({ creatingItem: false });
      }
    },

    async delete(id) {
      this.setUIFlag({ deletingItem: true });
      try {
        await CompanyAPI.delete(id);
        this.records = this.records.filter(r => r.id !== Number(id));
        if (this.activeCompanyId === Number(id)) this.resetCompanyDetailState();
        return Number(id);
      } catch (error) {
        return throwErrorMessage(error);
      } finally {
        this.setUIFlag({ deletingItem: false });
      }
    },

    async search({ search, page = 1, sort = 'name' }) {
      this.setUIFlag({ fetchingList: true });
      try {
        const {
          data: { payload, meta },
        } = await CompanyAPI.search(search, page, sort);
        this.records = camelizeCompany(payload);
        this.setMeta(meta);
        return this.records;
      } catch (error) {
        return throwErrorMessage(error);
      } finally {
        this.setUIFlag({ fetchingList: false });
      }
    },

    async deleteCompanyAvatar(companyId) {
      this.setUIFlag({ deletingAvatar: true });
      this.ensureActiveCompanyContext(companyId);
      try {
        const {
          data: { payload },
        } = await CompanyAPI.destroyAvatar(companyId);
        const company = camelizeCompany(payload);
        this.upsertCompanyRecord(company);
        return company;
      } catch (error) {
        return throwErrorMessage(error);
      } finally {
        this.setUIFlag({ deletingAvatar: false });
      }
    },

    async getCompanyContacts(companyId, page = 1) {
      this.setUIFlag({ fetchingContacts: true });
      this.ensureActiveCompanyContext(companyId);
      const activeCompanyId = Number(companyId);
      const requestToken = (this.companyContactsRequestToken || 0) + 1;
      this.companyContactsRequestToken = requestToken;

      try {
        const {
          data: { payload, meta },
        } = await CompanyAPI.listContacts(companyId, page);
        const contacts = camelizeContact(payload);
        const normalizedMeta = normalizeMeta(meta);

        if (
          this.companyContactsRequestToken !== requestToken ||
          this.activeCompanyId !== activeCompanyId
        ) {
          return contacts;
        }

        this.companyContacts = contacts;
        this.companyContactsMeta = normalizedMeta;
        this.updateCompanyContactsCount(companyId, normalizedMeta.totalCount);
        return contacts;
      } catch (error) {
        return throwErrorMessage(error);
      } finally {
        if (this.companyContactsRequestToken === requestToken) {
          this.setUIFlag({ fetchingContacts: false });
        }
      }
    },

    async getCompanyNotes(companyId) {
      this.setUIFlag({ fetchingNotes: true });
      this.ensureActiveCompanyContext(companyId);
      const activeCompanyId = Number(companyId);
      const requestToken = (this.companyNotesRequestToken || 0) + 1;
      this.companyNotesRequestToken = requestToken;

      try {
        const {
          data: { payload },
        } = await CompanyAPI.listNotes(companyId);
        const notes = camelcaseKeys(payload || [], { deep: true });

        if (
          this.companyNotesRequestToken !== requestToken ||
          this.activeCompanyId !== activeCompanyId
        ) {
          return notes;
        }

        this.companyNotes = notes;
        return notes;
      } catch (error) {
        return throwErrorMessage(error);
      } finally {
        if (
          this.companyNotesRequestToken === requestToken &&
          this.activeCompanyId === activeCompanyId
        ) {
          this.setUIFlag({ fetchingNotes: false });
        }
      }
    },

    async getCompanyConversations(companyId) {
      this.setUIFlag({ fetchingConversations: true });
      this.ensureActiveCompanyContext(companyId);
      const activeCompanyId = Number(companyId);
      const requestToken = (this.companyConversationsRequestToken || 0) + 1;
      this.companyConversationsRequestToken = requestToken;

      try {
        const {
          data: { payload },
        } = await CompanyAPI.listConversations(companyId);
        const conversations = camelcaseKeys(payload || [], { deep: true });

        if (
          this.companyConversationsRequestToken !== requestToken ||
          this.activeCompanyId !== activeCompanyId
        ) {
          return conversations;
        }

        this.companyConversations = conversations;
        return conversations;
      } catch (error) {
        return throwErrorMessage(error);
      } finally {
        if (
          this.companyConversationsRequestToken === requestToken &&
          this.activeCompanyId === activeCompanyId
        ) {
          this.setUIFlag({ fetchingConversations: false });
        }
      }
    },

    async searchCompanyContactCandidates({ companyId, search, page = 1 }) {
      const query = search?.trim() || '';
      if (!query) {
        this.clearContactSearchResults();
        return [];
      }

      this.setUIFlag({ searchingContacts: true });
      this.ensureActiveCompanyContext(companyId);
      this.activeContactSearchQuery = query;
      const activeCompanyId = Number(companyId);
      const requestToken = (this.contactSearchRequestToken || 0) + 1;
      this.contactSearchRequestToken = requestToken;

      try {
        const {
          data: { payload, meta },
        } = await CompanyAPI.searchContacts(companyId, query, page);
        const contacts = camelizeContact(payload);
        const normalizedMeta = normalizeMeta(meta);

        if (
          this.contactSearchRequestToken !== requestToken ||
          this.activeCompanyId !== activeCompanyId ||
          this.activeContactSearchQuery !== query
        ) {
          return contacts;
        }

        this.contactSearchResults = contacts;
        this.contactSearchMeta = normalizedMeta;
        return contacts;
      } catch (error) {
        return throwErrorMessage(error);
      } finally {
        if (this.contactSearchRequestToken === requestToken) {
          this.setUIFlag({ searchingContacts: false });
        }
      }
    },

    async attachContactToCompany(companyId, contactId) {
      this.setUIFlag({ creatingContact: true });
      this.ensureActiveCompanyContext(companyId);
      const activeCompanyId = Number(companyId);
      try {
        const {
          data: { payload },
        } = await CompanyAPI.createContact(companyId, {
          contact_id: contactId,
        });
        const contact = camelizeContact(payload);
        if (this.activeCompanyId === activeCompanyId) {
          await this.getCompanyContacts(companyId, 1);
          if (this.activeCompanyId === activeCompanyId) {
            this.clearContactSearchResults();
          }
        }
        return contact;
      } catch (error) {
        return throwErrorMessage(error);
      } finally {
        this.setUIFlag({ creatingContact: false });
      }
    },

    async removeContactFromCompany(companyId, contactId, page = null) {
      this.setUIFlag({ removingContact: true });
      this.ensureActiveCompanyContext(companyId);
      const activeCompanyId = Number(companyId);
      try {
        await CompanyAPI.removeContact(companyId, contactId);
        if (this.activeCompanyId === activeCompanyId) {
          await this.getCompanyContacts(
            companyId,
            page || this.companyContactsMeta?.page || 1
          );
        }
        return Number(contactId);
      } catch (error) {
        return throwErrorMessage(error);
      } finally {
        this.setUIFlag({ removingContact: false });
      }
    },

    async deleteCustomAttributes({ id, customAttributes }) {
      this.setUIFlag({ deletingCustomAttributes: true });
      try {
        const {
          data: { payload },
        } = await CompanyAPI.destroyCustomAttributes(id, customAttributes);
        const company = camelizeCompany(payload);
        this.upsertCompanyRecord(company);
        return company;
      } catch (error) {
        return throwErrorMessage(error);
      } finally {
        this.setUIFlag({ deletingCustomAttributes: false });
      }
    },

    resetCompanyDetailState() {
      const { fetchingList } = this.uiFlags;
      this.activeCompanyId = null;
      this.companyDetailRequestToken =
        (this.companyDetailRequestToken || 0) + 1;
      this.companyContactsRequestToken =
        (this.companyContactsRequestToken || 0) + 1;
      this.companyConversationsRequestToken =
        (this.companyConversationsRequestToken || 0) + 1;
      this.companyNotesRequestToken = (this.companyNotesRequestToken || 0) + 1;
      this.contactSearchRequestToken =
        (this.contactSearchRequestToken || 0) + 1;
      this.companyContacts = [];
      this.companyContactsMeta = {};
      this.companyConversations = [];
      this.companyNotes = [];
      this.contactSearchResults = [];
      this.contactSearchMeta = {};
      this.activeContactSearchQuery = '';
      this.uiFlags = { ...createInitialUIFlags(), fetchingList };
    },
  }),
});
