package ru.soune.nocopy.service.file.cloud;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import ru.soune.nocopy.entity.file.FileEntity;
import ru.soune.nocopy.repository.FileEntityRepository;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.AwsCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.core.ResponseBytes;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.core.sync.ResponseTransformer;
import software.amazon.awssdk.http.apache.ApacheHttpClient;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
import software.amazon.awssdk.services.s3.model.GetObjectResponse;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Map;
import java.util.UUID;

@Service
@Slf4j
@RequiredArgsConstructor
public class CloudStorageService {

    private final FileEntityRepository fileEntityRepository;

    @Value("${yandex.cloud.key}")
    private String key;

    @Value("${yandex.cloud.secret-key}")
    private String secretKey;

    @Value("${yandex.cloud.region}")
    private String region;

    @Value("${yandex.cloud.s3-endpoint}")
    private String s3endpoint;

    @Value("${yandex.cloud.bucket}")
    private String bucket;

    @Value("${yandex.cloud.bucket-cold}")
    private String bucketCold;

    public void saveFilesToStorage(String mimeType, String extension, String sendToCloudFilePath) {
        AwsCredentials credentials = AwsBasicCredentials.create(key, secretKey);
        S3Client s3Client = S3Client.builder()
                .httpClient(ApacheHttpClient.create())
                .region(Region.of(region))
                .endpointOverride(URI.create(s3endpoint))
                .credentialsProvider(StaticCredentialsProvider.create(credentials))
                .build();

        String mimeTypeFromFile = mimeType.equals("document") ? "application" : mimeType;
        String contentType = mimeTypeFromFile + "/" + extension;
        String filePath = "files" + sendToCloudFilePath;

        PutObjectRequest putObjectRequest = PutObjectRequest.builder()
                .bucket(bucket)
                .key(filePath)
                .contentType(contentType)
                .build();
        try {
            byte[] bytes = Files.readAllBytes(Paths.get(sendToCloudFilePath));
            s3Client.putObject(putObjectRequest,
                    RequestBody.fromBytes(bytes));
            Files.deleteIfExists(Paths.get(sendToCloudFilePath));
        } catch (IOException e) {
            log.error("Failed to read file: {}", filePath, e);
        }
    }

    public Map<String, String> saveSplitPartsToColdStorage(Long userId, Path file1Path, Path file2Path) {
        AwsCredentials credentials = AwsBasicCredentials.create(key, secretKey);
        S3Client s3Client = S3Client.builder()
                .httpClient(ApacheHttpClient.create())
                .region(Region.of(region))
                .endpointOverride(URI.create(s3endpoint))
                .credentialsProvider(StaticCredentialsProvider.create(credentials))
                .build();

        String part1Key = uploadFileToBucket(s3Client, bucketCold, file1Path, userId, "1");
        String part2Key = uploadFileToBucket(s3Client, bucketCold, file2Path, userId, "2");

        try {
            Files.deleteIfExists(file1Path);
            Files.deleteIfExists(file2Path);
            log.info("Deleted local split files for userId: {}", userId);
        } catch (IOException e) {
            log.error("Failed to delete local split files for userId: {}", userId, e);
        }

        return Map.of("part1", part1Key, "part2", part2Key);
    }

    public String downloadPartFromBucket(String bucket, String cloudKey) {
        AwsCredentials credentials = AwsBasicCredentials.create(key, secretKey);
        S3Client s3Client = S3Client.builder()
                .httpClient(ApacheHttpClient.create())
                .region(Region.of(region))
                .endpointOverride(URI.create(s3endpoint))
                .credentialsProvider(StaticCredentialsProvider.create(credentials))
                .build();

        try {
            GetObjectRequest getObjectRequest = GetObjectRequest.builder()
                    .bucket(bucket)
                    .key(cloudKey)
                    .build();

            ResponseBytes<GetObjectResponse> objectBytes = s3Client.getObjectAsBytes(getObjectRequest);
            String content = new String(objectBytes.asByteArray(), StandardCharsets.UTF_8);

            log.info("Downloaded from cold bucket, key: {}, size: {} chars", cloudKey, content.length());
            return content;

        } catch (Exception e) {
            log.error("Failed to download from cold bucket, key: {}", cloudKey, e);
            throw new RuntimeException("Failed to download file from cold storage", e);
        }
    }

    private String uploadFileToBucket(S3Client s3Client, String bucket, Path filePath, Long userId, String partSuffix) {
        String cloudKey = String.format("passport/%d/part_%s.txt", userId, partSuffix);

        try {
            byte[] bytes = Files.readAllBytes(filePath);

            PutObjectRequest putObjectRequest = PutObjectRequest.builder()
                    .bucket(bucket)
                    .key(cloudKey)
                    .contentType("text/plain")
                    .build();

            s3Client.putObject(putObjectRequest, RequestBody.fromBytes(bytes));
            log.info("Uploaded part {} to cold bucket, key: {}", partSuffix, cloudKey);
            return cloudKey;
        } catch (IOException e) {
            log.error("Failed to read file for upload: {}", filePath, e);
            throw new RuntimeException("Failed to upload split part to cold storage", e);
        }
    }

    public File readFileFromStorageByPath(String filePath) throws IOException {
        GetObjectRequest objectRequest = GetObjectRequest.builder()
                .bucket(bucket)
                .key("files" + filePath)
                .build();
        AwsCredentials credentials = AwsBasicCredentials.create(key, secretKey);

        S3Client s3Client = S3Client.builder()
                .httpClient(ApacheHttpClient.create())
                .region(Region.of(region))
                .endpointOverride(URI.create(s3endpoint))
                .credentialsProvider(StaticCredentialsProvider.create(credentials))
                .build();

        String tempDir = System.getProperty("java.io.tmpdir");

        String fileName = System.currentTimeMillis() + "_" + UUID.randomUUID() + "_" +
                filePath.replace("/", "_");
        File tempFile = new File(tempDir, fileName);

        s3Client.getObject(objectRequest, tempFile.toPath());

        return tempFile;
    }

    public byte[] readFileFromStorageBytes(String filePath) {
        AwsCredentials credentials = AwsBasicCredentials.create(key, secretKey);

        S3Client s3Client = S3Client.builder()
                .httpClient(ApacheHttpClient.create())
                .region(Region.of(region))
                .endpointOverride(URI.create(s3endpoint))
                .credentialsProvider(StaticCredentialsProvider.create(credentials))
                .build();

        GetObjectRequest objectRequest = GetObjectRequest.builder()
                .bucket(bucket)
                .key("files" + filePath)
                .build();

        try {
            ResponseBytes<GetObjectResponse> responseBytes = s3Client.getObjectAsBytes(objectRequest);

            return responseBytes.asByteArray();
        } catch (Exception e) {
            log.error("Failed to read file from S3: {}", filePath, e);
            throw new RuntimeException("Failed to read file from S3", e);
        }
    }


    public InputStream readFileFromStorage(String filePath) {
        GetObjectRequest objectRequest = GetObjectRequest.builder()
                .bucket(bucket)
                .key("files" + filePath)
                .build();

        AwsCredentials credentials = AwsBasicCredentials.create(key, secretKey);

        S3Client s3Client = S3Client.builder()
                .httpClient(ApacheHttpClient.create())
                .region(Region.of(region))
                .endpointOverride(URI.create(s3endpoint))
                .credentialsProvider(StaticCredentialsProvider.create(credentials))
                .build();

        return s3Client.getObject(objectRequest, ResponseTransformer.toInputStream());
    }

    public void deleteFileFromStorage(String filePath) {
        if (filePath == null || filePath.isEmpty()) {
            log.warn("Attempted to delete file with empty path");
            return;
        }

        AwsCredentials credentials = AwsBasicCredentials.create(key, secretKey);

        S3Client s3Client = S3Client.builder()
                .httpClient(ApacheHttpClient.create())
                .region(Region.of(region))
                .endpointOverride(URI.create(s3endpoint))
                .credentialsProvider(StaticCredentialsProvider.create(credentials))
                .build();

        String cloudKey = "files" + filePath;

        try {
            try {
                s3Client.headObject(builder -> builder.bucket(bucket).key(cloudKey).build());
            } catch (Exception e) {
                log.warn("File does not exist in cloud storage: {}", cloudKey);
                return;
            }

            s3Client.deleteObject(builder -> builder.bucket(bucket).key(cloudKey).build());

            log.info("File successfully deleted from cloud storage: {}", cloudKey);

        } catch (Exception e) {
            log.error("Failed to delete file from cloud storage: {}", cloudKey, e);
            throw new RuntimeException("Failed to delete file from cloud storage: " + cloudKey, e);
        }
    }

    public void deleteFromStorageDisk(FileEntity fileEntity) {
        deleteFileFromStorage(fileEntity.getFilePath());

        if (fileEntity.getProtectedFilePath() != null) {
            deleteFileFromStorage(fileEntity.getProtectedFilePath());
        }

        fileEntityRepository.delete(fileEntity);
    }
}
