SET ANSI_NULLS ON;
GO
SET QUOTED_IDENTIFIER ON;
GO

IF OBJECT_ID(N'dbo.EmailQueue', N'U') IS NULL
BEGIN
    CREATE TABLE dbo.EmailQueue
    (
        EmailQueueID bigint IDENTITY(1,1) NOT NULL CONSTRAINT PK_EmailQueue PRIMARY KEY,
        ToEmail nvarchar(256) NOT NULL,
        ToName nvarchar(200) NULL,
        FromEmail nvarchar(256) NOT NULL,
        FromName nvarchar(200) NULL,
        Subject nvarchar(300) NOT NULL,
        PlainTextBody nvarchar(max) NOT NULL,
        HtmlBody nvarchar(max) NULL,
        Status nvarchar(30) NOT NULL CONSTRAINT DF_EmailQueue_Status DEFAULT N'Pending',
        AttemptCount int NOT NULL CONSTRAINT DF_EmailQueue_AttemptCount DEFAULT 0,
        MaxAttempts int NOT NULL CONSTRAINT DF_EmailQueue_MaxAttempts DEFAULT 5,
        NextAttemptUtc datetime2 NOT NULL CONSTRAINT DF_EmailQueue_NextAttemptUtc DEFAULT SYSUTCDATETIME(),
        LastAttemptUtc datetime2 NULL,
        SentUtc datetime2 NULL,
        LastError nvarchar(max) NULL,
        CreatedUtc datetime2 NOT NULL CONSTRAINT DF_EmailQueue_CreatedUtc DEFAULT SYSUTCDATETIME(),
        UpdatedUtc datetime2 NOT NULL CONSTRAINT DF_EmailQueue_UpdatedUtc DEFAULT SYSUTCDATETIME(),
        CONSTRAINT CK_EmailQueue_Status CHECK (Status IN (N'Pending', N'Sending', N'Sent', N'Failed'))
    );
END;
GO

IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'IX_EmailQueue_StatusNextAttempt' AND object_id = OBJECT_ID(N'dbo.EmailQueue'))
    CREATE INDEX IX_EmailQueue_StatusNextAttempt ON dbo.EmailQueue(Status, NextAttemptUtc) INCLUDE (AttemptCount, MaxAttempts, CreatedUtc);
GO

IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'IX_EmailQueue_CreatedUtc' AND object_id = OBJECT_ID(N'dbo.EmailQueue'))
    CREATE INDEX IX_EmailQueue_CreatedUtc ON dbo.EmailQueue(CreatedUtc);
GO

CREATE OR ALTER PROCEDURE dbo.EmailQueue_Create
    @ToEmail nvarchar(256),
    @ToName nvarchar(200) = NULL,
    @FromEmail nvarchar(256),
    @FromName nvarchar(200) = NULL,
    @Subject nvarchar(300),
    @PlainTextBody nvarchar(max),
    @HtmlBody nvarchar(max) = NULL,
    @MaxAttempts int = 5
AS
BEGIN
    SET NOCOUNT ON;

    INSERT INTO dbo.EmailQueue
    (
        ToEmail,
        ToName,
        FromEmail,
        FromName,
        Subject,
        PlainTextBody,
        HtmlBody,
        Status,
        AttemptCount,
        MaxAttempts,
        NextAttemptUtc,
        CreatedUtc,
        UpdatedUtc
    )
    VALUES
    (
        @ToEmail,
        @ToName,
        @FromEmail,
        @FromName,
        @Subject,
        @PlainTextBody,
        @HtmlBody,
        N'Pending',
        0,
        @MaxAttempts,
        SYSUTCDATETIME(),
        SYSUTCDATETIME(),
        SYSUTCDATETIME()
    );

    SELECT CAST(SCOPE_IDENTITY() AS bigint);
END;
GO

CREATE OR ALTER PROCEDURE dbo.EmailQueue_DequeueBatch
    @BatchSize int = 10
AS
BEGIN
    SET NOCOUNT ON;

    IF @BatchSize IS NULL OR @BatchSize <= 0
        SET @BatchSize = 10;

    ;WITH NextEmails AS
    (
        SELECT TOP (@BatchSize) *
        FROM dbo.EmailQueue WITH (UPDLOCK, READPAST, ROWLOCK)
        WHERE Status = N'Pending'
            AND NextAttemptUtc <= SYSUTCDATETIME()
            AND AttemptCount < MaxAttempts
        ORDER BY CreatedUtc, EmailQueueID
    )
    UPDATE NextEmails
    SET Status = N'Sending',
        LastAttemptUtc = SYSUTCDATETIME(),
        UpdatedUtc = SYSUTCDATETIME()
    OUTPUT inserted.EmailQueueID, inserted.ToEmail, inserted.ToName, inserted.FromEmail, inserted.FromName,
        inserted.Subject, inserted.PlainTextBody, inserted.HtmlBody, inserted.Status, inserted.AttemptCount,
        inserted.MaxAttempts, inserted.NextAttemptUtc, inserted.LastAttemptUtc, inserted.SentUtc,
        inserted.LastError, inserted.CreatedUtc, inserted.UpdatedUtc;
END;
GO

CREATE OR ALTER PROCEDURE dbo.EmailQueue_MarkSending
    @EmailQueueID bigint
AS
BEGIN
    SET NOCOUNT ON;

    UPDATE dbo.EmailQueue
    SET Status = N'Sending',
        LastAttemptUtc = SYSUTCDATETIME(),
        UpdatedUtc = SYSUTCDATETIME()
    WHERE EmailQueueID = @EmailQueueID
        AND Status = N'Pending'
        AND AttemptCount < MaxAttempts;
END;
GO

CREATE OR ALTER PROCEDURE dbo.EmailQueue_MarkSent
    @EmailQueueID bigint
AS
BEGIN
    SET NOCOUNT ON;

    UPDATE dbo.EmailQueue
    SET Status = N'Sent',
        SentUtc = SYSUTCDATETIME(),
        LastError = NULL,
        UpdatedUtc = SYSUTCDATETIME()
    WHERE EmailQueueID = @EmailQueueID;
END;
GO

CREATE OR ALTER PROCEDURE dbo.EmailQueue_MarkFailed
    @EmailQueueID bigint,
    @LastError nvarchar(max)
AS
BEGIN
    SET NOCOUNT ON;

    DECLARE @Now datetime2 = SYSUTCDATETIME();

    UPDATE dbo.EmailQueue
    SET AttemptCount = AttemptCount + 1,
        LastAttemptUtc = @Now,
        LastError = @LastError,
        Status = CASE WHEN AttemptCount + 1 >= MaxAttempts THEN N'Failed' ELSE N'Pending' END,
        NextAttemptUtc = CASE
            WHEN AttemptCount + 1 >= MaxAttempts THEN NextAttemptUtc
            WHEN AttemptCount + 1 = 1 THEN DATEADD(minute, 1, @Now)
            WHEN AttemptCount + 1 = 2 THEN DATEADD(minute, 5, @Now)
            ELSE DATEADD(minute, 15, @Now)
        END,
        UpdatedUtc = @Now
    WHERE EmailQueueID = @EmailQueueID;
END;
GO

CREATE OR ALTER PROCEDURE dbo.EmailQueue_ResetStuckSending
AS
BEGIN
    SET NOCOUNT ON;

    DECLARE @Now datetime2 = SYSUTCDATETIME();
    DECLARE @Cutoff datetime2 = DATEADD(minute, -10, @Now);

    UPDATE dbo.EmailQueue
    SET Status = CASE WHEN AttemptCount >= MaxAttempts THEN N'Failed' ELSE N'Pending' END,
        NextAttemptUtc = CASE WHEN AttemptCount >= MaxAttempts THEN NextAttemptUtc ELSE @Now END,
        UpdatedUtc = @Now
    WHERE Status = N'Sending'
        AND LastAttemptUtc IS NOT NULL
        AND LastAttemptUtc < @Cutoff;
END;
GO
