package com.haha.service.impl;

import com.alibaba.fastjson2.JSON;
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.haha.common.constant.CheckinConstants;
import com.haha.entity.dto.CheckinQueryDTO;
import com.haha.entity.dto.CheckinSubmitDTO;
import com.haha.entity.CheckinRecord;
import com.haha.entity.Device;
import com.haha.entity.Shop;
import com.haha.mapper.CheckinRecordMapper;
import com.haha.mapper.DeviceMapper;
import com.haha.mapper.ShopMapper;
import com.haha.service.CheckinRecordService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * 签到服务实现类
 */
@Slf4j
@Service
public class CheckinRecordServiceImpl extends ServiceImpl<CheckinRecordMapper, CheckinRecord>
        implements CheckinRecordService {

    @Autowired
    private CheckinRecordMapper checkinRecordMapper;

    @Autowired
    private ShopMapper shopMapper;

    @Autowired
    private DeviceMapper deviceMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public CheckinRecord submitCheckin(CheckinSubmitDTO dto, Long userId, String userName, String userNo) {
        log.info("用户[{}]提交签到, 类型: {}", userName, dto.getType());

        CheckinRecord record = new CheckinRecord();
        record.setCheckinType(dto.getType());
        record.setUserId(userId);
        record.setUserName(userName);
        record.setUserNo(userNo);
        record.setShopId(dto.getShopId());
        record.setDeviceId(dto.getDeviceId());
        record.setLatitude(dto.getLocation().getLatitude());
        record.setLongitude(dto.getLocation().getLongitude());
        record.setAddress(dto.getLocation().getAddress());
        record.setAccuracy(dto.getLocation().getAccuracy());
        record.setRemark(dto.getRemark());
        record.setOfflineId(dto.getOfflineId());
        record.setCheckinTime(LocalDateTime.now());
        record.setStatus(CheckinConstants.STATUS_SYNCED);
        record.setSyncedAt(LocalDateTime.now());
        record.setCreateTime(LocalDateTime.now());
        record.setUpdateTime(LocalDateTime.now());

        if (dto.getShopId() != null) {
            Shop shop = shopMapper.selectById(dto.getShopId());
            if (shop != null) {
                record.setShopName(shop.getName());
            }
        }

        if (StringUtils.hasText(dto.getDeviceId())) {
            Device device = deviceMapper.selectByDeviceId(dto.getDeviceId());
            if (device != null) {
                record.setDeviceName(device.getName());
            }
        }

        if (dto.getPhotos() != null && !dto.getPhotos().isEmpty()) {
            List<Map<String, Object>> photoList = new ArrayList<>();
            for (CheckinSubmitDTO.PhotoDTO photo : dto.getPhotos()) {
                Map<String, Object> photoMap = new HashMap<>();
                photoMap.put("id", photo.getId());
                photoMap.put("path", photo.getPath());
                photoMap.put("watermarkPath", photo.getWatermarkPath());
                photoMap.put("timestamp", photo.getTimestamp());
                photoList.add(photoMap);
            }
            record.setPhotos(JSON.toJSONString(photoList));
        }

        save(record);

        log.info("签到记录创建成功, ID: {}", record.getId());
        return record;
    }

    @Override
    public IPage<Map<String, Object>> getCheckinPage(CheckinQueryDTO query) {
        query.validate();
        Page<Map<String, Object>> page = new Page<>(query.getPage(), query.getPageSize());

        IPage<Map<String, Object>> result = checkinRecordMapper.selectCheckinPage(
                page,
                query.getType(),
                query.getUserId(),
                query.getShopId(),
                query.getStatus(),
                query.getStartDate(),
                query.getEndDate()
        );

        for (Map<String, Object> record : result.getRecords()) {
            processCheckinRecord(record);
        }

        return result;
    }

    @Override
    public Map<String, Object> getCheckinDetail(Long id) {
        CheckinRecord record = getById(id);
        if (record == null) {
            return null;
        }

        Map<String, Object> result = new HashMap<>();
        result.put("id", record.getId());
        result.put("checkinType", record.getCheckinType());
        result.put("typeName", getTypeName(record.getCheckinType()));
        result.put("userId", record.getUserId());
        result.put("userName", record.getUserName());
        result.put("userNo", record.getUserNo());
        result.put("shopId", record.getShopId());
        result.put("shopName", record.getShopName());
        result.put("deviceId", record.getDeviceId());
        result.put("deviceName", record.getDeviceName());
        result.put("latitude", record.getLatitude());
        result.put("longitude", record.getLongitude());
        result.put("address", record.getAddress());
        result.put("accuracy", record.getAccuracy());
        result.put("remark", record.getRemark());
        result.put("status", record.getStatus());
        result.put("offlineId", record.getOfflineId());
        result.put("checkinTime", formatDateTime(record.getCheckinTime()));
        result.put("syncedAt", formatDateTime(record.getSyncedAt()));
        result.put("createTime", formatDateTime(record.getCreateTime()));

        if (StringUtils.hasText(record.getPhotos())) {
            result.put("photos", JSON.parseArray(record.getPhotos()));
        } else {
            result.put("photos", new ArrayList<>());
        }

        Map<String, Object> location = new HashMap<>();
        location.put("latitude", record.getLatitude());
        location.put("longitude", record.getLongitude());
        location.put("address", record.getAddress());
        location.put("accuracy", record.getAccuracy());
        result.put("location", location);

        return result;
    }

    @Override
    public Map<String, Object> getCheckinStats() {
        Map<String, Object> stats = checkinRecordMapper.selectCheckinStats();

        stats.put("todayCount", stats.getOrDefault("today_count", 0));
        stats.put("weekCount", stats.getOrDefault("week_count", 0));
        stats.put("monthCount", stats.getOrDefault("month_count", 0));
        stats.put("inventoryTallyCount", stats.getOrDefault("inventory_tally_count", 0));
        stats.put("deliveryReplenishCount", stats.getOrDefault("delivery_replenish_count", 0));
        stats.put("pendingSyncCount", stats.getOrDefault("pending_sync_count", 0));

        stats.remove("today_count");
        stats.remove("week_count");
        stats.remove("month_count");
        stats.remove("inventory_tally_count");
        stats.remove("delivery_replenish_count");
        stats.remove("pending_sync_count");

        return stats;
    }

    @Override
    public Map<String, Object> getTodayCheckin(Long userId) {
        Map<String, Object> result = new HashMap<>();
        result.put("hasInventoryTally", false);
        result.put("hasDeliveryReplenish", false);

        List<CheckinRecord> todayRecords = checkinRecordMapper.selectTodayCheckinByUser(userId);

        for (CheckinRecord record : todayRecords) {
            if (CheckinConstants.TYPE_INVENTORY_TALLY.equals(record.getCheckinType())) {
                result.put("hasInventoryTally", true);
                result.put("inventoryTallyTime", formatDateTime(record.getCheckinTime()));
            } else if (CheckinConstants.TYPE_REPLENISHMENT.equals(record.getCheckinType())) {
                result.put("hasDeliveryReplenish", true);
                result.put("deliveryReplenishTime", formatDateTime(record.getCheckinTime()));
            }
        }

        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> syncOfflineCheckins(List<CheckinRecord> records) {
        log.info("开始同步离线签到记录, 数量: {}", records.size());

        int successCount = 0;
        int failedCount = 0;
        List<String> failedIds = new ArrayList<>();

        // 1. 收集所有 offlineId，批量查询已存在记录（1 次 SELECT IN）
        List<String> offlineIds = records.stream()
                .map(CheckinRecord::getOfflineId)
                .filter(StringUtils::hasText).toList();
        java.util.Set<String> existingOfflineIds = new java.util.HashSet<>();
        if (!offlineIds.isEmpty()) {
            List<CheckinRecord> existingRecords = checkinRecordMapper.selectList(
                    new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<CheckinRecord>()
                            .in(CheckinRecord::getOfflineId, offlineIds));
            for (CheckinRecord r : existingRecords) {
                if (StringUtils.hasText(r.getOfflineId())) {
                    existingOfflineIds.add(r.getOfflineId());
                }
            }
        }

        // 2. 分离已存在和新记录
        List<CheckinRecord> newRecords = new ArrayList<>();
        for (CheckinRecord record : records) {
            try {
                if (StringUtils.hasText(record.getOfflineId())) {
                    if (existingOfflineIds.contains(record.getOfflineId())) {
                        log.info("离线签到记录已存在, offlineId: {}", record.getOfflineId());
                        successCount++;
                        continue;
                    }
                }

                record.setStatus(CheckinConstants.STATUS_SYNCED);
                record.setSyncedAt(LocalDateTime.now());
                record.setUpdateTime(LocalDateTime.now());

                if (record.getCreateTime() == null) {
                    record.setCreateTime(LocalDateTime.now());
                }

                newRecords.add(record);
                successCount++;
            } catch (Exception e) {
                log.error("同步离线签到记录失败: {}", e.getMessage(), e);
                failedCount++;
                if (StringUtils.hasText(record.getOfflineId())) {
                    failedIds.add(record.getOfflineId());
                }
            }
        }

        // 3. 批量保存新记录（1 次批量 INSERT）
        if (!newRecords.isEmpty()) {
            saveBatch(newRecords);
        }

        Map<String, Object> result = new HashMap<>();
        result.put("success", successCount);
        result.put("failed", failedCount);
        result.put("failedIds", failedIds);

        log.info("离线签到同步完成, 成功: {}, 失败: {}", successCount, failedCount);
        return result;
    }

    @Override
    public String exportCheckinRecords(CheckinQueryDTO query) {
        log.info("导出签到记录, 条件: {}", query);
        return "https://oss.example.com/exports/checkin_records_" + System.currentTimeMillis() + ".xlsx";
    }

    @Override
    public boolean hasTodayCheckin(Long userId, String checkinType) {
        return checkinRecordMapper.countTodayCheckinByUserAndType(userId, checkinType) > 0;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> syncOfflineCheckinsFromData(List<Map<String, Object>> recordsData, Long userId) {
        List<CheckinRecord> records = new ArrayList<>();
        for (Map<String, Object> data : recordsData) {
            CheckinRecord record = new CheckinRecord();
            record.setCheckinType((String) data.get("type"));
            record.setUserId(userId);
            record.setUserName((String) data.get("userName"));
            record.setUserNo((String) data.get("userNo"));
            record.setShopId(data.get("shopId") != null ? Long.valueOf(data.get("shopId").toString()) : null);
            record.setDeviceId((String) data.get("deviceId"));

            @SuppressWarnings("unchecked")
            Map<String, Object> location = (Map<String, Object>) data.get("location");
            if (location != null) {
                record.setLatitude(new java.math.BigDecimal(location.get("latitude").toString()));
                record.setLongitude(new java.math.BigDecimal(location.get("longitude").toString()));
                record.setAddress((String) location.get("address"));
                record.setAccuracy(location.get("accuracy") != null ? Integer.valueOf(location.get("accuracy").toString()) : null);
            }

            @SuppressWarnings("unchecked")
            List<Map<String, Object>> photos = (List<Map<String, Object>>) data.get("photos");
            if (photos != null) {
                record.setPhotos(JSON.toJSONString(photos));
            }

            record.setRemark((String) data.get("remark"));
            record.setOfflineId((String) data.get("offlineId"));
            record.setStatus(CheckinConstants.STATUS_PENDING);

            String createdAt = (String) data.get("createdAt");
            if (createdAt != null) {
                try {
                    record.setCheckinTime(LocalDateTime.parse(createdAt.replace(" ", "T")));
                    record.setCreateTime(record.getCheckinTime());
                } catch (Exception e) {
                    record.setCheckinTime(LocalDateTime.now());
                    record.setCreateTime(LocalDateTime.now());
                }
            } else {
                record.setCheckinTime(LocalDateTime.now());
                record.setCreateTime(LocalDateTime.now());
            }

            records.add(record);
        }

        return syncOfflineCheckins(records);
    }

    private void processCheckinRecord(Map<String, Object> record) {
        record.put("typeName", getTypeName((String) record.get("checkin_type")));

        if (record.get("checkin_time") != null) {
            record.put("checkinTime", formatDateTime(record.get("checkin_time")));
        }
        if (record.get("synced_at") != null) {
            record.put("syncedAt", formatDateTime(record.get("synced_at")));
        }
        if (record.get("create_time") != null) {
            record.put("createdAt", formatDateTime(record.get("create_time")));
        }

        Object photos = record.get("photos");
        if (photos != null && StringUtils.hasText(photos.toString())) {
            record.put("photos", JSON.parseArray(photos.toString()));
        } else {
            record.put("photos", new ArrayList<>());
        }

        Map<String, Object> location = new HashMap<>();
        location.put("latitude", record.get("latitude"));
        location.put("longitude", record.get("longitude"));
        location.put("address", record.get("address"));
        location.put("accuracy", record.get("accuracy"));
        record.put("location", location);
    }

    private String getTypeName(String type) {
        return CheckinConstants.getTypeName(type);
    }

    private String formatDateTime(Object dateTime) {
        if (dateTime == null) {
            return null;
        }
        if (dateTime instanceof LocalDateTime) {
            return ((LocalDateTime) dateTime).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        }
        return dateTime.toString().replace("T", " ").substring(0, Math.min(19, dateTime.toString().length()));
    }
}
