import { MembershipRole } from "@prisma/client";

import type { CloseComFieldOptions, CloseComLead } from "@calcom/lib/CloseCom";
import CloseCom from "@calcom/lib/CloseCom";
import { getCloseComContactIds, getCloseComLeadId, getCustomFieldsIds } from "@calcom/lib/CloseComeUtils";
import logger from "@calcom/lib/logger";
import type { TeamInfoType } from "@calcom/lib/sync/ISyncService";
import SyncServiceCore from "@calcom/lib/sync/ISyncService";
import type { ConsoleUserInfoType, WebUserInfoType } from "@calcom/lib/sync/ISyncService";
import type ISyncService from "@calcom/lib/sync/ISyncService";

// Cal.com Custom Contact Fields
const calComCustomContactFields: CloseComFieldOptions = [
  // Field name, field type, required?, multiple values?
  ["Username", "text", false, false],
  ["Plan", "text", true, false],
  ["Last booking", "date", false, false],
  ["Created at", "date", true, false],
];

const calComSharedFields: CloseComFieldOptions = [["Contact Role", "text", false, false]];

const serviceName = "closecom_service";

export default class CloseComService extends SyncServiceCore implements ISyncService {
  protected declare service: CloseCom;

  constructor() {
    super(serviceName, CloseCom, logger.getChildLogger({ prefix: [`[[sync] ${serviceName}`] }));
  }

  upsertAnyUser = async (
    user: WebUserInfoType | ConsoleUserInfoType,
    leadInfo?: CloseComLead,
    role?: string
  ) => {
    this.log.debug("sync:closecom:user", { user });
    // Get Cal.com Lead
    const leadId = await getCloseComLeadId(this.service, leadInfo);
    this.log.debug("sync:closecom:user:leadId", { leadId });
    // Get Contacts ids: already creates contacts
    const [contactId] = await getCloseComContactIds([user], this.service, leadId);
    this.log.debug("sync:closecom:user:contactsIds", { contactId });
    // Get Custom Contact fields ids
    const customFieldsIds = await getCustomFieldsIds("contact", calComCustomContactFields, this.service);
    this.log.debug("sync:closecom:user:customFieldsIds", { customFieldsIds });
    // Get shared fields ids
    const sharedFieldsIds = await getCustomFieldsIds("shared", calComSharedFields, this.service);
    this.log.debug("sync:closecom:user:sharedFieldsIds", { sharedFieldsIds });
    const allFields = customFieldsIds.concat(sharedFieldsIds);
    this.log.debug("sync:closecom:user:allFields", { allFields });
    const lastBooking = "email" in user ? await this.getUserLastBooking(user) : null;
    this.log.debug("sync:closecom:user:lastBooking", { lastBooking });
    const username = "username" in user ? user.username : null;
    // Prepare values for each Custom Contact Fields
    const allFieldsValues = [
      username, // Username
      user.plan, // Plan
      lastBooking && lastBooking.booking
        ? new Date(lastBooking.booking.createdAt).toLocaleDateString("en-US")
        : null, // Last Booking
      user.createdDate,
      role === MembershipRole.OWNER ? "Point of Contact" : "",
    ];
    this.log.debug("sync:closecom:contact:allFieldsValues", { allFieldsValues });
    // Preparing Custom Activity Instance data for Close.com
    const person = Object.assign(
      {},
      {
        person: user,
        lead_id: leadId,
        contactId,
      },
      ...allFields.map((fieldId: string, index: number) => {
        return {
          [`custom.${fieldId}`]: allFieldsValues[index],
        };
      })
    );
    // Create Custom Activity type instance
    return await this.service.contact.update(person);
  };

  public console = {
    user: {
      upsert: async (consoleUser: ConsoleUserInfoType) => {
        return this.upsertAnyUser(consoleUser);
      },
    },
  };

  public web = {
    user: {
      upsert: async (webUser: WebUserInfoType) => {
        return this.upsertAnyUser(webUser);
      },
      delete: async (webUser: WebUserInfoType) => {
        this.log.debug("sync:closecom:web:user:delete", { webUser });
        const [contactId] = await getCloseComContactIds([webUser], this.service);
        this.log.debug("sync:closecom:web:user:delete:contactId", { contactId });
        if (contactId) {
          return this.service.contact.delete(contactId);
        } else {
          throw Error("Web user not found in service");
        }
      },
    },
    team: {
      create: async (team: TeamInfoType, webUser: WebUserInfoType, role: MembershipRole) => {
        return this.upsertAnyUser(
          webUser,
          {
            companyName: team.name,
          },
          role
        );
      },
      delete: async (team: TeamInfoType) => {
        this.log.debug("sync:closecom:web:team:delete", { team });
        const leadId = await getCloseComLeadId(this.service, { companyName: team.name });
        this.log.debug("sync:closecom:web:team:delete:leadId", { leadId });
        this.service.lead.delete(leadId);
      },
      update: async (prevTeam: TeamInfoType, updatedTeam: TeamInfoType) => {
        this.log.debug("sync:closecom:web:team:update", { prevTeam, updatedTeam });
        const leadId = await getCloseComLeadId(this.service, { companyName: prevTeam.name });
        this.log.debug("sync:closecom:web:team:update:leadId", { leadId });
        this.service.lead.update(leadId, { companyName: updatedTeam.name });
      },
    },
    membership: {
      delete: async (webUser: WebUserInfoType) => {
        this.log.debug("sync:closecom:web:membership:delete", { webUser });
        const [contactId] = await getCloseComContactIds([webUser], this.service);
        this.log.debug("sync:closecom:web:membership:delete:contactId", { contactId });
        if (contactId) {
          return this.service.contact.delete(contactId);
        } else {
          throw Error("Web user not found in service");
        }
      },
    },
  };
}
