package database

import (
	"time"

	"github.com/google/uuid"
	"gorm.io/gorm"
)

const (
	CAMPAIGN_TABLE = "campaigns"
)

// Campaign is gorm data model
type Campaign struct {
	ID        *uuid.UUID `gorm:"primary_key;not null;unique;type:uuid"`
	CreatedAt *time.Time `gorm:"not null;index;"`
	UpdatedAt *time.Time `gorm:"not null;index;"`

	CloseAt      *time.Time `gorm:"index;"`
	ClosedAt     *time.Time `gorm:"index;"`
	AnonymizeAt  *time.Time `gorm:"index;"`
	AnonymizedAt *time.Time `gorm:"index;"`
	// DataAnonymizeAt schedules anonymization of submitted data, user agent, ip
	// and browser metadata while keeping the recipient relation intact.
	DataAnonymizeAt *time.Time `gorm:"index;"`
	// DataAnonymizedAt is set when submitted data has been anonymized.
	DataAnonymizedAt *time.Time `gorm:"index;"`
	SortField        string     `gorm:";"`
	SortOrder        string     `gorm:";"` // 'asc,desc,random'
	SendStartAt      *time.Time `gorm:"index;"`
	SendEndAt        *time.Time `gorm:"index;"`
	// ScheduleAt is set when the campaign uses late-scheduling.
	// the task runner will call schedule() when now >= ScheduleAt.
	// null means the campaign was scheduled immediately at creation.
	ScheduleAt *time.Time `gorm:"index;"`
	// JitterMin and JitterMax are persisted only when ScheduleAt is set (late-scheduling).
	// They are cleared after the campaign is scheduled by the task runner.
	// For immediately-scheduled campaigns these columns remain null.
	JitterMin *int `gorm:""`
	JitterMax *int `gorm:""`

	// ConstraintWeekDays is a binary format.
	// 0b00000001 = 1 = sunday
	// 0b00000010 = 2 = monday
	// 0b00000100 = 4 = tuesday
	// 0b00001000 = 8 = ...
	// 0b00010000 = 16 =
	// 0b00100000 = 32 =
	// 0b01000000 = 64 =
	ConstraintWeekDays *int `gorm:";"`
	// hh:mm
	ConstraintStartTime *string `gorm:"index;"`
	// hh:mm
	ConstraintEndTime   *string `gorm:"index;"`
	SaveSubmittedData   bool    `gorm:"not null;default:false"`
	SaveBrowserMetadata bool    `gorm:"not null;default:false"`
	IsAnonymous         bool    `gorm:"not null;default:false"`
	IsTest              bool    `gorm:"not null;default:false"`
	Obfuscate           bool    `gorm:"not null;default:false"`

	// deprecated: webhook settings moved to campaign_webhooks junction table
	// kept for backward compatibility during migration
	WebhookIncludeData string `gorm:"not null;default:'full'"`
	WebhookEvents      int    `gorm:"not null;default:0"`

	// Snapshotted from the campaign template while the campaign holds no
	// recipients. Read from here rather than the template so a later addition to
	// a self managed campaign matches the recipients already scheduled, and a
	// template edit cannot change the form of URLs already delivered.
	LureURLMode    string `gorm:"not null;default:'query'"`
	LureCodeAlgo   string `gorm:"not null;default:'crockford32'"`
	LureCodeLength int    `gorm:"not null;default:12"`

	// has one
	CampaignTemplateID *uuid.UUID `gorm:"index;type:uuid;"`
	CampaignTemplate   *CampaignTemplate

	// can has one
	CompanyID     *uuid.UUID `gorm:"index;type:uuid;index;uniqueIndex:idx_campaigns_unique_name_and_company_id;"`
	Company       *Company
	DenyPageID    *uuid.UUID `gorm:"type:uuid;index;"`
	DenyPage      *Page      `gorm:"foreignKey:DenyPageID;references:ID"`
	EvasionPageID *uuid.UUID `gorm:"type:uuid;index;"`
	EvasionPage   *Page      `gorm:"foreignKey:EvasionPageID;references:ID"`
	// NotableEventID notable event for this campaign
	NotableEvent   *Event     `gorm:"foreignKey:NotableEventID;references:ID"`
	NotableEventID *uuid.UUID `gorm:"type:uuid;index"`

	// deprecated: kept for backward compatibility during migration
	WebhookID *uuid.UUID `gorm:"type:uuid;index;"`

	// has many-to-many
	RecipientGroups []*RecipientGroup `gorm:"many2many:campaign_recipient_groups"`
	AllowDeny       []*AllowDeny      `gorm:"many2many:campaign_allow_denies"`
	Webhooks        []*Webhook        `gorm:"many2many:campaign_webhooks"`

	Name string `gorm:"not null;uniqueIndex:idx_campaigns_unique_name_and_company_id"`
}

func (c *Campaign) Migrate(db *gorm.DB) error {
	// SQLITE
	// ensure name + company id is unique
	return UniqueIndexNameAndNullCompanyID(db, "campaigns")
}

func (Campaign) TableName() string {
	return CAMPAIGN_TABLE
}
