package com.writeoff.module.system.service;

import com.writeoff.security.AuthContext;
import com.writeoff.security.PasswordCodecService;
import com.writeoff.security.PasswordPolicyService;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentMatcher;
import org.springframework.jdbc.core.JdbcTemplate;

import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

class SystemUserServicePasswordTest {

    @AfterEach
    void tearDown() {
        AuthContext.clear();
    }

    @Test
    void shouldAcceptLegacyPlaintextOldPasswordAndUpgradeToHashWhenChangingPassword() {
        JdbcTemplate jdbcTemplate = mock(JdbcTemplate.class);
        PasswordCodecService passwordCodecService = new PasswordCodecService();
        SystemUserService systemUserService = new SystemUserService(
                jdbcTemplate,
                null,
                new PasswordPolicyService(),
                passwordCodecService
        );
        AuthContext.set(1001L, 2001L);
        when(jdbcTemplate.queryForObject(
                eq("SELECT COUNT(1) FROM sys_user WHERE tenant_id=? AND id=? AND is_deleted=0"),
                eq(Integer.class),
                eq(2001L),
                eq(1001L)
        )).thenReturn(1);
        when(jdbcTemplate.queryForObject(
                eq("SELECT password_hash FROM sys_user WHERE tenant_id=? AND id=? AND is_deleted=0 LIMIT 1"),
                eq(String.class),
                eq(2001L),
                eq(1001L)
        )).thenReturn("legacy-plain-password");

        systemUserService.changeMyPassword(1001L, "legacy-plain-password", "Abcd1234!");

        verify(jdbcTemplate).update(
                eq("UPDATE sys_user SET password_hash=?, updated_at=CURRENT_TIMESTAMP WHERE tenant_id=? AND id=?"),
                argThat(matchesEncodedPassword("Abcd1234!", passwordCodecService)),
                eq(2001L),
                eq(1001L)
        );
    }

    private ArgumentMatcher<Object> matchesEncodedPassword(String rawPassword, PasswordCodecService passwordCodecService) {
        return value -> value instanceof String
                && passwordCodecService.isEncoded((String) value)
                && passwordCodecService.matches(rawPassword, (String) value);
    }
}
