package com.seer.teach.iot.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.seer.teach.common.enums.ResultCodeEnum;
import com.seer.teach.common.enums.iot.DeviceStatusEnum;
import com.seer.teach.common.enums.iot.IsInfoEnum;
import com.seer.teach.common.resp.AddressIdResp;
import com.seer.teach.common.service.RegionAddressService;
import com.seer.teach.common.utils.AssertUtils;
import com.seer.teach.iot.common.dto.CityPercentageDTO;
import com.seer.teach.iot.common.req.AuthReq;
import com.seer.teach.iot.common.resp.LoginResp;
import com.seer.teach.iot.convert.DevicesConvert;
import com.seer.teach.iot.module.entity.AppKeySecretsEntity;
import com.seer.teach.iot.module.entity.DeviceModelsEntity;
import com.seer.teach.iot.module.entity.DevicesEntity;
import com.seer.teach.iot.module.entity.IotVersionsEntity;
import com.seer.teach.iot.module.mapper.DevicesMapper;
import com.seer.teach.iot.service.*;
import com.seer.teach.user.api.UserInfoServiceApi;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import jakarta.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * <p>
 * 设备服务实现类，用于管理设备相关信息的业务逻辑操作
 * </p>
 *
 * @author Captain
 * @since 2025-05-19
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class DevicesServiceImpl extends ServiceImpl<DevicesMapper, DevicesEntity> implements DevicesService {

    private final AppKeySecretsService appKeySecretsService;

    private final DeviceModelsService deviceModelsService;

    private final UserInfoServiceApi userInfoServiceApi;

    private final UserDevicesService userDevicesService;

    private final IotVersionsService versionsService;

    private final RegionAddressService regionAddressService;

    /**
     * 根据设备唯一标识查询完整的设备实体对象。
     *
     * @param unionId 设备唯一标识
     * @return {@link DevicesEntity} 设备实体对象
     * @throws IllegalArgumentException 如果设备不存在，将抛出 {@link ResultCodeEnum#DEVICE_NOT_FOUND} 异常
     */
    @Override
    public DevicesEntity getDeviceEntityByDevice(String unionId) {
        DevicesEntity one = this.getOne(new LambdaQueryWrapper<DevicesEntity>()
                .eq(DevicesEntity::getUnionId, unionId));
        AssertUtils.notNull(one, ResultCodeEnum.DEVICE_NOT_FOUND);
        return one;
    }

    @Override
    public List<Map<String, Object>> countDevicesByModel() {
        return this.getBaseMapper().countDevicesByModel();
    }

    @Override
    public List<CityPercentageDTO> selectCityPercentages(List<Integer> cityIds) {
        return this.getBaseMapper().selectCityPercentages(cityIds);
    }

    @Override
    public IPage<DevicesEntity> pageList(Page<DevicesEntity> pageParm, Wrapper<DevicesEntity> wrapper) {
        IPage<DevicesEntity> resultPage = this.page(pageParm, wrapper);
        log.info("分页查询结果:{}", resultPage);
        return resultPage;
    }

    /**
     * 用户登录接口实现。
     * <p>
     * 根据传入的认证参数进行设备查询，如果设备不存在则创建新设备并绑定默认用户，
     * 否则直接使用已有设备关联的用户完成登录，并返回登录响应信息。
     *
     * @param params      认证请求参数，包括 accessKey 和 unionId 等信息
     * @param request     HTTP 请求对象，用于获取客户端 IP 地址等信息
     * @param modelName   设备型号名称
     * @param versionCode 版本号
     * @return 登录响应对象，包含 token 和是否需要完善信息标识
     */
    @Override
    @Transactional(timeout = 30, rollbackFor = Exception.class)
    public LoginResp login(AuthReq params,
                           HttpServletRequest request,
                           String modelName,
                           Integer versionCode) {
        // 查询应用密钥信息
        AppKeySecretsEntity appKey = appKeySecretsService.getByAppKey(params.getAccessKey());

        // 根据 appId 和 unionId 查询设备是否存在
        DevicesEntity device = this.lambdaQuery()
                .eq(DevicesEntity::getAppId, appKey.getId())
                .eq(DevicesEntity::getUnionId, params.getUnionId())
                .one();

        log.info("设备是否存在: {}", device != null);

        // 若设备不存在，则注册新设备、创建默认用户并绑定
        if (Objects.isNull(device)) {
            Integer deviceId = saveDevice(params.getUnionId(), request, modelName, versionCode, appKey);
            Integer userId = userInfoServiceApi.saveDefaultChildrenUser();
            userDevicesService.bindDevice(userId, deviceId);
            StpUtil.login(userId);
            log.info("设备 {} 注册成功", deviceId);
            return new LoginResp(
                    StpUtil.getTokenInfo().getTokenValue(),
                    IsInfoEnum.NO_INFO.getCode()
            );
        }

        // 获取已存在设备对应的用户 ID 并检查用户信息完整性
        Integer userId = userDevicesService.getUserIdByDeviceId(device.getId());
        Integer isInfo = userInfoServiceApi.checkUserInfo(userId);

        // 执行登录操作并记录日志
        StpUtil.login(userId);
        log.info("设备 {} 登录成功", device.getId());

        // 返回登录结果
        return new LoginResp(
                StpUtil.getTokenInfo().getTokenValue(),
                isInfo
        );
    }

    /**
     * 保存新的设备信息。
     * <p>
     * 包括根据型号名查找或新增设备型号、根据版本码获取版本信息、解析 IP 归属地，
     * 最终构建并保存设备实体。
     *
     * @param request     HTTP 请求对象，用于提取客户端 IP
     * @param modelName   设备型号名称
     * @param versionCode 版本编号
     * @param appKey      应用密钥实体
     * @return 新增设备的主键 ID
     */
    private Integer saveDevice(String unionId,
                               HttpServletRequest request,
                               String modelName,
                               Integer versionCode,
                               AppKeySecretsEntity appKey) {
        // 获取或保存设备型号
        DeviceModelsEntity model = deviceModelsService.getOrSaveByModelName(modelName);

        // 获取版本信息
        IotVersionsEntity version = versionsService.getByVersionCode(versionCode);

        // 解析客户端 IP 及归属地信息
        String ip = regionAddressService.getIpAddress(request);
        AddressIdResp addressId = regionAddressService.getAddressId(ip);

        // 转换为设备实体并保存
        DevicesEntity device = DevicesConvert.INSTANCE.toDeviceEntity(
                unionId,
                appKey.getId(),
                ip,
                model.getId(),
                version.getId(),
                addressId.getCityId(),
                DeviceStatusEnum.ENABLE.getCode()
        );
        this.save(device);
        log.info("保存设备信息: {}", device);
        return device.getId();
    }


}