package com.saas.tenant.service;

import com.saas.tenant.dto.request.CreatePhysicalResourceRequest;
import com.saas.tenant.dto.request.UpdatePhysicalResourceRequest;
import com.saas.tenant.dto.response.PhysicalResourceResponse;
import com.saas.tenant.entity.PhysicalResource;
import com.saas.tenant.entity.ResourceStatus;
import com.saas.tenant.entity.ResourceType;
import com.saas.tenant.repository.PhysicalResourceRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
@Slf4j
public class PhysicalResourceService {

    private final PhysicalResourceRepository resourceRepository;

    @Transactional
    public PhysicalResourceResponse createResource(CreatePhysicalResourceRequest request) {
        log.info("Creating new physical resource: {}", request.getName());

        PhysicalResource resource = new PhysicalResource();
        resource.setName(request.getName());
        resource.setIdentifier(request.getIdentifier());
        resource.setType(request.getType());
        resource.setStatus(request.getStatus());
        resource.setDepartment(request.getDepartment());
        resource.setDescription(request.getDescription());
        resource.setLocation(request.getLocation());
        resource.setCapacity(request.getCapacity());
        resource.setFloorNumber(request.getFloorNumber());

        PhysicalResource savedResource = resourceRepository.save(resource);
        log.info("Physical resource created successfully with ID: {}", savedResource.getId());

        return mapToResponse(savedResource);
    }

    @Transactional
    public PhysicalResourceResponse updateResource(Long id, UpdatePhysicalResourceRequest request) {
        log.info("Updating physical resource with ID: {}", id);

        PhysicalResource resource = resourceRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("Physical resource not found with ID: " + id));

        if (request.getName() != null)
            resource.setName(request.getName());
        if (request.getIdentifier() != null)
            resource.setIdentifier(request.getIdentifier());
        if (request.getType() != null)
            resource.setType(request.getType());
        if (request.getStatus() != null)
            resource.setStatus(request.getStatus());
        if (request.getDepartment() != null)
            resource.setDepartment(request.getDepartment());
        if (request.getDescription() != null)
            resource.setDescription(request.getDescription());
        if (request.getLocation() != null)
            resource.setLocation(request.getLocation());
        if (request.getCapacity() != null)
            resource.setCapacity(request.getCapacity());
        if (request.getFloorNumber() != null)
            resource.setFloorNumber(request.getFloorNumber());

        PhysicalResource updatedResource = resourceRepository.save(resource);
        log.info("Physical resource updated successfully: {}", updatedResource.getId());

        return mapToResponse(updatedResource);
    }

    @Transactional(readOnly = true)
    public PhysicalResourceResponse getResourceById(Long id) {
        log.info("Fetching physical resource with ID: {}", id);
        PhysicalResource resource = resourceRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("Physical resource not found with ID: " + id));
        return mapToResponse(resource);
    }

    @Transactional(readOnly = true)
    public List<PhysicalResourceResponse> getAllResources() {
        log.info("Fetching all physical resources");
        return resourceRepository.findAll().stream()
                .map(this::mapToResponse)
                .collect(Collectors.toList());
    }

    @Transactional(readOnly = true)
    public List<PhysicalResourceResponse> getResourcesByType(ResourceType type) {
        log.info("Fetching resources by type: {}", type);
        return resourceRepository.findByType(type).stream()
                .map(this::mapToResponse)
                .collect(Collectors.toList());
    }

    @Transactional(readOnly = true)
    public List<PhysicalResourceResponse> getResourcesByStatus(ResourceStatus status) {
        log.info("Fetching resources by status: {}", status);
        return resourceRepository.findByStatus(status).stream()
                .map(this::mapToResponse)
                .collect(Collectors.toList());
    }

    @Transactional(readOnly = true)
    public List<PhysicalResourceResponse> getAvailableResources() {
        log.info("Fetching available resources");
        return resourceRepository.findAvailableResources().stream()
                .map(this::mapToResponse)
                .collect(Collectors.toList());
    }

    @Transactional(readOnly = true)
    public List<PhysicalResourceResponse> getAvailableResourcesByType(ResourceType type) {
        log.info("Fetching available resources by type: {}", type);
        return resourceRepository.findAvailableResourcesByType(type).stream()
                .map(this::mapToResponse)
                .collect(Collectors.toList());
    }

    @Transactional
    public void deleteResource(Long id) {
        log.info("Deleting physical resource with ID: {}", id);
        PhysicalResource resource = resourceRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("Physical resource not found with ID: " + id));

        // Soft delete - set status to OUT_OF_SERVICE
        resource.setStatus(ResourceStatus.OUT_OF_SERVICE);
        resourceRepository.save(resource);
        log.info("Physical resource soft-deleted successfully: {}", id);
    }

    private PhysicalResourceResponse mapToResponse(PhysicalResource resource) {
        return PhysicalResourceResponse.builder()
                .id(resource.getId())
                .name(resource.getName())
                .identifier(resource.getIdentifier())
                .type(resource.getType())
                .status(resource.getStatus())
                .department(resource.getDepartment())
                .description(resource.getDescription())
                .location(resource.getLocation())
                .capacity(resource.getCapacity())
                .floorNumber(resource.getFloorNumber())
                .createdAt(resource.getCreatedAt())
                .updatedAt(resource.getUpdatedAt())
                .build();
    }
}
