import {
  Sequelize,
  DataTypes,
  Model,
  InferAttributes,
  InferCreationAttributes,
  CreationOptional,
} from "sequelize";
import type {
  BallotSecrecy,
  ElectionSecurityConfig,
  ElectionStatus,
  EligibilityModel,
  ResultsVisibility,
  VotingMethod,
} from "@/types";
import { parseJsonColumn } from "@/lib/utils/parse-json-column";

export class Election extends Model<
  InferAttributes<Election>,
  InferCreationAttributes<Election>
> {
  declare id: CreationOptional<string>;
  declare workspaceId: string;
  declare title: string;
  declare description: string | null;
  declare slug: string;
  declare status: ElectionStatus;
  declare ballotSecrecy: BallotSecrecy;
  declare votingMethod: VotingMethod;
  declare eligibilityModel: EligibilityModel;
  declare allowProxyVoting: CreationOptional<boolean>;
  declare requiresAuthorizationRecord: CreationOptional<boolean>;
  declare recordDate: string | null;
  declare quorumRequired: CreationOptional<boolean>;
  declare quorumCount: number | null;
  declare securityConfig: ElectionSecurityConfig | null;
  declare digitalSignatureRequired: CreationOptional<boolean>;
  declare resultsVisibility: ResultsVisibility;
  declare postBallotSurveyId: string | null;
  declare isTestMode: CreationOptional<boolean>;
  declare openEnrollment: CreationOptional<boolean>;
  declare startAt: Date | null;
  declare endAt: Date | null;
  declare createdBy: string | null;
  declare publishedAt: Date | null;
  declare closedAt: Date | null;
  declare certifiedAt: Date | null;
  declare createdAt: CreationOptional<Date>;
  declare updatedAt: CreationOptional<Date>;
}

export function initElectionModel(sequelize: Sequelize) {
  Election.init(
    {
      id: {
        type: DataTypes.UUID,
        defaultValue: DataTypes.UUIDV4,
        primaryKey: true,
      },
      workspaceId: { type: DataTypes.UUID, allowNull: false },
      title: { type: DataTypes.STRING(500), allowNull: false },
      description: { type: DataTypes.TEXT, allowNull: true },
      slug: { type: DataTypes.STRING(128), allowNull: false, unique: true },
      status: {
        type: DataTypes.ENUM("draft", "open", "closed", "certified"),
        allowNull: false,
        defaultValue: "draft",
      },
      ballotSecrecy: {
        type: DataTypes.ENUM("secret", "open"),
        allowNull: false,
        defaultValue: "secret",
      },
      votingMethod: {
        type: DataTypes.ENUM(
          "plurality",
          "ranked_choice_irv",
          "single_transferable_vote",
          "approval",
          "condorcet",
          "cumulative",
          "weighted"
        ),
        allowNull: false,
        defaultValue: "plurality",
      },
      eligibilityModel: {
        type: DataTypes.ENUM("member", "property", "shareholder", "custom"),
        allowNull: false,
        defaultValue: "member",
      },
      allowProxyVoting: {
        type: DataTypes.BOOLEAN,
        allowNull: false,
        defaultValue: false,
      },
      requiresAuthorizationRecord: {
        type: DataTypes.BOOLEAN,
        allowNull: false,
        defaultValue: false,
      },
      recordDate: { type: DataTypes.DATEONLY, allowNull: true },
      quorumRequired: {
        type: DataTypes.BOOLEAN,
        allowNull: false,
        defaultValue: false,
      },
      quorumCount: { type: DataTypes.INTEGER, allowNull: true },
      securityConfig: {
        type: DataTypes.JSON,
        allowNull: true,
        get() {
          return parseJsonColumn<ElectionSecurityConfig>(
            this.getDataValue("securityConfig")
          );
        },
        set(value: ElectionSecurityConfig | string | null) {
          this.setDataValue(
            "securityConfig",
            typeof value === "string"
              ? parseJsonColumn<ElectionSecurityConfig>(value)
              : value
          );
        },
      },
      digitalSignatureRequired: {
        type: DataTypes.BOOLEAN,
        allowNull: false,
        defaultValue: false,
      },
      resultsVisibility: {
        type: DataTypes.ENUM("after_close", "admin_realtime"),
        allowNull: false,
        defaultValue: "after_close",
      },
      postBallotSurveyId: { type: DataTypes.UUID, allowNull: true },
      isTestMode: {
        type: DataTypes.BOOLEAN,
        allowNull: false,
        defaultValue: false,
      },
      openEnrollment: {
        type: DataTypes.BOOLEAN,
        allowNull: false,
        defaultValue: false,
      },
      startAt: { type: DataTypes.DATE, allowNull: true },
      endAt: { type: DataTypes.DATE, allowNull: true },
      createdBy: { type: DataTypes.UUID, allowNull: true },
      publishedAt: { type: DataTypes.DATE, allowNull: true },
      closedAt: { type: DataTypes.DATE, allowNull: true },
      certifiedAt: { type: DataTypes.DATE, allowNull: true },
      createdAt: DataTypes.DATE,
      updatedAt: DataTypes.DATE,
    },
    { sequelize, modelName: "Election", tableName: "Elections" }
  );
  return Election;
}
