SET ANSI_NULLS ON;
GO
SET QUOTED_IDENTIFIER ON;
GO

IF COL_LENGTH(N'dbo.UserTwoFactor', N'ConfirmedUtc') IS NULL
    ALTER TABLE dbo.UserTwoFactor ADD ConfirmedUtc datetime2 NULL;
GO

IF COL_LENGTH(N'dbo.UserTwoFactor', N'LastUsedTimeStep') IS NULL
    ALTER TABLE dbo.UserTwoFactor ADD LastUsedTimeStep bigint NULL;
GO

CREATE OR ALTER PROCEDURE dbo.UserTwoFactor_Get
    @UserID int
AS
BEGIN
    SET NOCOUNT ON;

    SELECT UserID, SecretKey, RecoveryCodes, CreatedUtc, ConfirmedUtc, LastUsedTimeStep
    FROM dbo.UserTwoFactor
    WHERE UserID = @UserID;
END;
GO

CREATE OR ALTER PROCEDURE dbo.UserTwoFactor_Save
    @UserID int,
    @SecretKey nvarchar(500),
    @RecoveryCodes nvarchar(max) = NULL,
    @ConfirmedUtc datetime2 = NULL
AS
BEGIN
    SET NOCOUNT ON;

    MERGE dbo.UserTwoFactor AS target
    USING (SELECT @UserID AS UserID, @SecretKey AS SecretKey, @RecoveryCodes AS RecoveryCodes, @ConfirmedUtc AS ConfirmedUtc) AS source
        ON target.UserID = source.UserID
    WHEN MATCHED THEN
        UPDATE SET SecretKey = source.SecretKey,
            RecoveryCodes = source.RecoveryCodes,
            ConfirmedUtc = source.ConfirmedUtc,
            LastUsedTimeStep = NULL
    WHEN NOT MATCHED THEN
        INSERT (UserID, SecretKey, RecoveryCodes, ConfirmedUtc)
        VALUES (source.UserID, source.SecretKey, source.RecoveryCodes, source.ConfirmedUtc);

    EXEC dbo.UserTwoFactor_Get @UserID = @UserID;
END;
GO

CREATE OR ALTER PROCEDURE dbo.UserTwoFactor_Enable
    @UserID int
AS
BEGIN
    SET NOCOUNT ON;

    UPDATE dbo.UserTwoFactor
    SET ConfirmedUtc = COALESCE(ConfirmedUtc, SYSUTCDATETIME())
    WHERE UserID = @UserID;

    UPDATE dbo.AppUser
    SET TwoFactorEnabled = 1,
        UpdatedUtc = SYSUTCDATETIME()
    WHERE UserID = @UserID;

    EXEC dbo.User_GetById @UserID = @UserID;
END;
GO

CREATE OR ALTER PROCEDURE dbo.UserTwoFactor_Disable
    @UserID int
AS
BEGIN
    SET NOCOUNT ON;

    UPDATE dbo.AppUser
    SET TwoFactorEnabled = 0,
        UpdatedUtc = SYSUTCDATETIME()
    WHERE UserID = @UserID;

    DELETE FROM dbo.UserTwoFactor
    WHERE UserID = @UserID;

    EXEC dbo.User_GetById @UserID = @UserID;
END;
GO

CREATE OR ALTER PROCEDURE dbo.UserTwoFactor_SaveRecoveryCodes
    @UserID int,
    @RecoveryCodes nvarchar(max)
AS
BEGIN
    SET NOCOUNT ON;

    UPDATE dbo.UserTwoFactor
    SET RecoveryCodes = @RecoveryCodes
    WHERE UserID = @UserID;

    EXEC dbo.UserTwoFactor_Get @UserID = @UserID;
END;
GO

CREATE OR ALTER PROCEDURE dbo.UserTwoFactor_GetRecoveryCodes
    @UserID int
AS
BEGIN
    SET NOCOUNT ON;

    SELECT RecoveryCodes
    FROM dbo.UserTwoFactor
    WHERE UserID = @UserID;
END;
GO

CREATE OR ALTER PROCEDURE dbo.UserTwoFactor_UpdateLastUsedTimeStep
    @UserID int,
    @LastUsedTimeStep bigint
AS
BEGIN
    SET NOCOUNT ON;

    UPDATE dbo.UserTwoFactor
    SET LastUsedTimeStep = @LastUsedTimeStep
    WHERE UserID = @UserID
        AND (LastUsedTimeStep IS NULL OR LastUsedTimeStep < @LastUsedTimeStep);

    SELECT CAST(CASE WHEN @@ROWCOUNT = 1 THEN 1 ELSE 0 END AS bit);
END;
GO

CREATE OR ALTER PROCEDURE dbo.User_GetTwoFactor
    @UserID int
AS
BEGIN
    SET NOCOUNT ON;

    EXEC dbo.UserTwoFactor_Get @UserID = @UserID;
END;
GO

CREATE OR ALTER PROCEDURE dbo.User_SaveTwoFactor
    @UserID int,
    @SecretKey nvarchar(500),
    @RecoveryCodes nvarchar(max) = NULL
AS
BEGIN
    SET NOCOUNT ON;

    EXEC dbo.UserTwoFactor_Save
        @UserID = @UserID,
        @SecretKey = @SecretKey,
        @RecoveryCodes = @RecoveryCodes,
        @ConfirmedUtc = NULL;
END;
GO
