package com.reading.platform.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.reading.platform.common.enums.ErrorCode;
import com.reading.platform.common.enums.UserRole;
import com.reading.platform.common.exception.BusinessException;
import com.reading.platform.common.security.JwtPayload;
import com.reading.platform.common.security.JwtTokenProvider;
import com.reading.platform.common.security.SecurityUtils;
import com.reading.platform.dto.request.LoginRequest;
import com.reading.platform.dto.response.LoginResponse;
import com.reading.platform.dto.response.UserInfoResponse;
import com.reading.platform.entity.AdminUser;
import com.reading.platform.entity.Parent;
import com.reading.platform.entity.Teacher;
import com.reading.platform.mapper.AdminUserMapper;
import com.reading.platform.mapper.ParentMapper;
import com.reading.platform.mapper.TeacherMapper;
import com.reading.platform.service.AuthService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;

@Slf4j
@Service
@RequiredArgsConstructor
public class AuthServiceImpl implements AuthService {

    private final AdminUserMapper adminUserMapper;
    private final TeacherMapper teacherMapper;
    private final ParentMapper parentMapper;
    private final JwtTokenProvider jwtTokenProvider;
    private final PasswordEncoder passwordEncoder;

    @Override
    public LoginResponse login(LoginRequest request) {
        String username = request.getUsername();
        String password = request.getPassword();
        String role = request.getRole();

        // If role is specified, login with that role
        if (role != null && !role.isEmpty()) {
            return loginWithRole(username, password, role);
        }

        // Try to login as admin first
        AdminUser adminUser = adminUserMapper.selectOne(
                new LambdaQueryWrapper<AdminUser>().eq(AdminUser::getUsername, username)
        );
        if (adminUser != null) {
            if (!passwordEncoder.matches(password, adminUser.getPassword())) {
                throw new BusinessException(ErrorCode.LOGIN_FAILED);
            }
            if (!"active".equals(adminUser.getStatus())) {
                throw new BusinessException(ErrorCode.ACCOUNT_DISABLED);
            }
            // Update last login time
            adminUser.setLastLoginAt(LocalDateTime.now());
            adminUserMapper.updateById(adminUser);

            JwtPayload payload = JwtPayload.builder()
                    .userId(adminUser.getId())
                    .username(adminUser.getUsername())
                    .role("admin")
                    .tenantId(null)
                    .name(adminUser.getName())
                    .build();

            return LoginResponse.builder()
                    .token(jwtTokenProvider.generateToken(payload))
                    .userId(adminUser.getId())
                    .username(adminUser.getUsername())
                    .name(adminUser.getName())
                    .role("admin")
                    .tenantId(null)
                    .build();
        }

        // Try teacher
        Teacher teacher = teacherMapper.selectOne(
                new LambdaQueryWrapper<Teacher>().eq(Teacher::getUsername, username)
        );
        if (teacher != null) {
            if (!passwordEncoder.matches(password, teacher.getPassword())) {
                throw new BusinessException(ErrorCode.LOGIN_FAILED);
            }
            if (!"active".equals(teacher.getStatus())) {
                throw new BusinessException(ErrorCode.ACCOUNT_DISABLED);
            }
            teacher.setLastLoginAt(LocalDateTime.now());
            teacherMapper.updateById(teacher);

            JwtPayload payload = JwtPayload.builder()
                    .userId(teacher.getId())
                    .username(teacher.getUsername())
                    .role("teacher")
                    .tenantId(teacher.getTenantId())
                    .name(teacher.getName())
                    .build();

            return LoginResponse.builder()
                    .token(jwtTokenProvider.generateToken(payload))
                    .userId(teacher.getId())
                    .username(teacher.getUsername())
                    .name(teacher.getName())
                    .role("teacher")
                    .tenantId(teacher.getTenantId())
                    .build();
        }

        // Try parent
        Parent parent = parentMapper.selectOne(
                new LambdaQueryWrapper<Parent>().eq(Parent::getUsername, username)
        );
        if (parent != null) {
            if (!passwordEncoder.matches(password, parent.getPassword())) {
                throw new BusinessException(ErrorCode.LOGIN_FAILED);
            }
            if (!"active".equals(parent.getStatus())) {
                throw new BusinessException(ErrorCode.ACCOUNT_DISABLED);
            }
            parent.setLastLoginAt(LocalDateTime.now());
            parentMapper.updateById(parent);

            JwtPayload payload = JwtPayload.builder()
                    .userId(parent.getId())
                    .username(parent.getUsername())
                    .role("parent")
                    .tenantId(parent.getTenantId())
                    .name(parent.getName())
                    .build();

            return LoginResponse.builder()
                    .token(jwtTokenProvider.generateToken(payload))
                    .userId(parent.getId())
                    .username(parent.getUsername())
                    .name(parent.getName())
                    .role("parent")
                    .tenantId(parent.getTenantId())
                    .build();
        }

        throw new BusinessException(ErrorCode.LOGIN_FAILED);
    }

    private LoginResponse loginWithRole(String username, String password, String role) {
        UserRole userRole = UserRole.fromCode(role);

        switch (userRole) {
            case ADMIN -> {
                AdminUser adminUser = adminUserMapper.selectOne(
                        new LambdaQueryWrapper<AdminUser>().eq(AdminUser::getUsername, username)
                );
                if (adminUser == null || !passwordEncoder.matches(password, adminUser.getPassword())) {
                    throw new BusinessException(ErrorCode.LOGIN_FAILED);
                }
                if (!"active".equals(adminUser.getStatus())) {
                    throw new BusinessException(ErrorCode.ACCOUNT_DISABLED);
                }
                adminUser.setLastLoginAt(LocalDateTime.now());
                adminUserMapper.updateById(adminUser);

                JwtPayload payload = JwtPayload.builder()
                        .userId(adminUser.getId())
                        .username(adminUser.getUsername())
                        .role("admin")
                        .tenantId(null)
                        .name(adminUser.getName())
                        .build();

                return LoginResponse.builder()
                        .token(jwtTokenProvider.generateToken(payload))
                        .userId(adminUser.getId())
                        .username(adminUser.getUsername())
                        .name(adminUser.getName())
                        .role("admin")
                        .tenantId(null)
                        .build();
            }
            case SCHOOL, TEACHER -> {
                Teacher teacher = teacherMapper.selectOne(
                        new LambdaQueryWrapper<Teacher>().eq(Teacher::getUsername, username)
                );
                if (teacher == null || !passwordEncoder.matches(password, teacher.getPassword())) {
                    throw new BusinessException(ErrorCode.LOGIN_FAILED);
                }
                if (!"active".equals(teacher.getStatus())) {
                    throw new BusinessException(ErrorCode.ACCOUNT_DISABLED);
                }
                teacher.setLastLoginAt(LocalDateTime.now());
                teacherMapper.updateById(teacher);

                JwtPayload payload = JwtPayload.builder()
                        .userId(teacher.getId())
                        .username(teacher.getUsername())
                        .role("teacher")
                        .tenantId(teacher.getTenantId())
                        .name(teacher.getName())
                        .build();

                return LoginResponse.builder()
                        .token(jwtTokenProvider.generateToken(payload))
                        .userId(teacher.getId())
                        .username(teacher.getUsername())
                        .name(teacher.getName())
                        .role("teacher")
                        .tenantId(teacher.getTenantId())
                        .build();
            }
            case PARENT -> {
                Parent parent = parentMapper.selectOne(
                        new LambdaQueryWrapper<Parent>().eq(Parent::getUsername, username)
                );
                if (parent == null || !passwordEncoder.matches(password, parent.getPassword())) {
                    throw new BusinessException(ErrorCode.LOGIN_FAILED);
                }
                if (!"active".equals(parent.getStatus())) {
                    throw new BusinessException(ErrorCode.ACCOUNT_DISABLED);
                }
                parent.setLastLoginAt(LocalDateTime.now());
                parentMapper.updateById(parent);

                JwtPayload payload = JwtPayload.builder()
                        .userId(parent.getId())
                        .username(parent.getUsername())
                        .role("parent")
                        .tenantId(parent.getTenantId())
                        .name(parent.getName())
                        .build();

                return LoginResponse.builder()
                        .token(jwtTokenProvider.generateToken(payload))
                        .userId(parent.getId())
                        .username(parent.getUsername())
                        .name(parent.getName())
                        .role("parent")
                        .tenantId(parent.getTenantId())
                        .build();
            }
            default -> throw new BusinessException(ErrorCode.LOGIN_FAILED);
        }
    }

    @Override
    public UserInfoResponse getCurrentUserInfo() {
        JwtPayload payload = SecurityUtils.getCurrentUser();
        String role = payload.getRole();

        return switch (role) {
            case "admin" -> {
                AdminUser adminUser = adminUserMapper.selectById(payload.getUserId());
                yield UserInfoResponse.builder()
                        .id(adminUser.getId())
                        .username(adminUser.getUsername())
                        .name(adminUser.getName())
                        .email(adminUser.getEmail())
                        .phone(adminUser.getPhone())
                        .avatarUrl(adminUser.getAvatarUrl())
                        .role("admin")
                        .tenantId(null)
                        .build();
            }
            case "teacher" -> {
                Teacher teacher = teacherMapper.selectById(payload.getUserId());
                yield UserInfoResponse.builder()
                        .id(teacher.getId())
                        .username(teacher.getUsername())
                        .name(teacher.getName())
                        .email(teacher.getEmail())
                        .phone(teacher.getPhone())
                        .avatarUrl(teacher.getAvatarUrl())
                        .role("teacher")
                        .tenantId(teacher.getTenantId())
                        .build();
            }
            case "parent" -> {
                Parent parent = parentMapper.selectById(payload.getUserId());
                yield UserInfoResponse.builder()
                        .id(parent.getId())
                        .username(parent.getUsername())
                        .name(parent.getName())
                        .email(parent.getEmail())
                        .phone(parent.getPhone())
                        .avatarUrl(parent.getAvatarUrl())
                        .role("parent")
                        .tenantId(parent.getTenantId())
                        .build();
            }
            default -> throw new BusinessException(ErrorCode.USER_NOT_FOUND);
        };
    }

    @Override
    public void changePassword(String oldPassword, String newPassword) {
        JwtPayload payload = SecurityUtils.getCurrentUser();
        String role = payload.getRole();
        Long userId = payload.getUserId();

        switch (role) {
            case "admin" -> {
                AdminUser adminUser = adminUserMapper.selectById(userId);
                if (!passwordEncoder.matches(oldPassword, adminUser.getPassword())) {
                    throw new BusinessException(ErrorCode.OLD_PASSWORD_ERROR);
                }
                adminUser.setPassword(passwordEncoder.encode(newPassword));
                adminUserMapper.updateById(adminUser);
            }
            case "teacher" -> {
                Teacher teacher = teacherMapper.selectById(userId);
                if (!passwordEncoder.matches(oldPassword, teacher.getPassword())) {
                    throw new BusinessException(ErrorCode.OLD_PASSWORD_ERROR);
                }
                teacher.setPassword(passwordEncoder.encode(newPassword));
                teacherMapper.updateById(teacher);
            }
            case "parent" -> {
                Parent parent = parentMapper.selectById(userId);
                if (!passwordEncoder.matches(oldPassword, parent.getPassword())) {
                    throw new BusinessException(ErrorCode.OLD_PASSWORD_ERROR);
                }
                parent.setPassword(passwordEncoder.encode(newPassword));
                parentMapper.updateById(parent);
            }
            default -> throw new BusinessException(ErrorCode.USER_NOT_FOUND);
        }
    }

}
