package mx.gob.jumapacelaya.api;

import com.nimbusds.jose.shaded.gson.*;
import com.vaadin.flow.server.VaadinService;
import mx.gob.jumapacelaya.models.JsonOrder;
import mx.gob.jumapacelaya.models.RedmineUser;
import mx.gob.jumapacelaya.models.Ticket;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Component
public class RedmineClient {

    private static final int PAGE_SIZE = 25;
    static String REDMINE_URL;
    static String API_KEY;
    public static final Gson GSON = new Gson();



    public RedmineClient(@Value("${redmine.url}") String redmineUrl, @Value("${redmine.api_key}") String apiKey) {
        REDMINE_URL = redmineUrl;
        API_KEY = apiKey;

    }

    //AQUI OBTENGO LOS TICKETS DESDE REDMINE
    public List<Ticket> getTickets(RedmineUser user, boolean includeClosed) {
        List<Ticket> tickets = new ArrayList<>();
        HttpClient client = HttpClient.newHttpClient();
        int offset = 0;

        // Si includeClose es true, incluira todos los tikets si no, incluira solo los que estan abiertos
        String statusFilter = includeClosed ? "&status_id=*" : "&status_id=open";


        while (true) {
            HttpRequest request = HttpRequest.newBuilder()
                    .uri(URI.create(REDMINE_URL + "/issues.json?key=" + user.getKey() + statusFilter + "&offset=" + offset))
                    .header("Content-Type", "application/json")
                    .build();

            try {
                HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
                if (response.statusCode() == 200) {
                    String responseBody = response.body();
                    List<Ticket> pageTickets = parseTickets(responseBody);
                    tickets.addAll(pageTickets);
                    if (pageTickets.size() < PAGE_SIZE) {
                        break;
                    }
                    offset += PAGE_SIZE;
                } else {
                    System.err.println("Error en la respuesta: " + response.statusCode());
                    break;
                }
            } catch (Exception e) {
                e.printStackTrace();
                break;
            }
        }
        System.out.println("Total tickets obtenidos: " + tickets.size());
        return tickets;
    }

    //AQUI OBTENGO LOS TICKETS DESDE REDMINE
    public List<Ticket> getTicketsAuthor(RedmineUser user) {
        List<Ticket> tickets = new ArrayList<>();
        HttpClient client = HttpClient.newHttpClient();
        int offset = 0;


        while (true) {
            HttpRequest request = HttpRequest.newBuilder()
                    .uri(URI.create(REDMINE_URL + "/issues.json?key=" + user.getKey() + "&author_id=" + user.getId() + "&offset=" + offset))
                    .header("Content-Type", "application/json")
                    .build();

            try {
                HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
                if (response.statusCode() == 200) {
                    String responseBody = response.body();
                    List<Ticket> pageTickets = parseTickets(responseBody);
                    tickets.addAll(pageTickets);
                    if (pageTickets.size() < PAGE_SIZE) {
                        break;
                    }
                    offset += PAGE_SIZE;
                } else {
                    System.err.println("Error en la respuesta: " + response.statusCode());
                    break;
                }
            } catch (Exception e) {
                e.printStackTrace();
                break;
            }
        }
        System.out.println("Total tickets obtenidos: " + tickets.size());
        return tickets;
    }

    // Aquí se parsean todos los tickets que existen y se les da un formato con los campos a mostrarse
    private List<Ticket> parseTickets(String json) {
        List<Ticket> tickets = new ArrayList<>();
        try {
            JsonObject jsonObject = JsonParser.parseString(json).getAsJsonObject();
            JsonArray issues = jsonObject.getAsJsonArray("issues");
            if (issues != null) {
                for (JsonElement issueElement : issues) {
                    JsonObject issue = issueElement.getAsJsonObject();

                    // Verifica y obtiene el ID
                    int id = issue.has("id") && !issue.get("id").isJsonNull() ? issue.get("id").getAsInt() : 0;

                    // Verifica y obtiene el subject
                    String subject = issue.has("subject") && !issue.get("subject").isJsonNull() ? issue.get("subject").getAsString() : "";

                    // Verifica y obtiene la descripción
                    String description = issue.has("description") && !issue.get("description").isJsonNull() ? issue.get("description").getAsString() : "";

                    // Verifica y obtiene el status
                    String status = "Unknown";
                    if (issue.has("status") && !issue.get("status").isJsonNull()) {
                        JsonObject statusObject = issue.getAsJsonObject("status");
                        if (statusObject.has("name") && !statusObject.get("name").isJsonNull()) {
                            status = statusObject.get("name").getAsString();
                        }
                    }

                    // Verifica y obtiene la fecha de creación
                    String dateString = issue.has("created_on") && !issue.get("created_on").isJsonNull() ? issue.get("created_on").getAsString() : "";
                    LocalDate date = null;
                    if (!dateString.isEmpty()) {
                        try {
                            DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE_TIME;
                            date = LocalDate.parse(dateString, formatter);
                        } catch (DateTimeParseException e) {
                            System.err.println("Error al parsear la fecha: " + dateString);
                            e.printStackTrace();
                        }
                    }

                    //Verifica y obtiene el ID del tipo de ticket
                    Integer trackerId = null;
                    if (issue.has("tracker") && !issue.get("tracker").isJsonNull()) {
                        JsonObject trackerObject = issue.getAsJsonObject("tracker");
                        if (trackerObject.has("id") && !trackerObject.get("id").isJsonNull()) {
                            trackerId = trackerObject.get("id").getAsInt();
                        }
                    }


                    // Agrega el ticket a la lista
                    tickets.add(new Ticket(id, subject, description, status, date != null ? date.toString() : "", trackerId));
                }
            } else {
                System.out.println("La respuesta JSON no contiene la clave 'issues'");
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("Ocurrió un error al parsear los tickets: " + e.getMessage());
        }
        return tickets;
    }




    private static class TicketResponse {
        private List<Ticket> issues;

        public List<Ticket> getIssues() {
            return issues;
        }

        public void setIssues(List<Ticket> issues) {
            this.issues = issues;
        }
    }


    //AQUI OBTENGO A LOS USUARIOS
    //Se devuelven en formato Json
    public RedmineUser getUserByUsername(String username) {
        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(REDMINE_URL + "/users.json?name=" + username ))
                .header("Content-Type", "application/json")
                .header("X-Redmine-API-Key", API_KEY)
                .build();

        try {
            HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
            if (response.statusCode() == 200) {
                String responseBody = response.body();
                System.out.println("Datos del usuario " + responseBody);
                return parseUser(responseBody);
            } else {
                System.err.println("Error en la respuesta: " + response.statusCode());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    //Aqui se parsean a los usuarios y se les da un formato con los campos a mostrarse
    private RedmineUser parseUser(String json) {
        JsonObject jsonObject = JsonParser.parseString(json).getAsJsonObject();
        JsonObject userJson = jsonObject.getAsJsonArray("users").get(0).getAsJsonObject();

        RedmineUser user = new RedmineUser();
        user.setId(userJson.get("id").getAsInt());
        user.setLogin(userJson.get("login").getAsString());
        user.setFirstname(userJson.get("firstname").getAsString());
        user.setLastname(userJson.get("lastname").getAsString());
        user.setMail(userJson.get("mail").getAsString());
        //user.setKey(userJson.get("key").getAsString());
        return user;
    }




    public RedmineUser getMyAccount(String username) {
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(REDMINE_URL + "/my/account.json"))
                .header("Content-Type", "application/json")
                .header("X-Redmine-Switch-User", username)
                .header("X-Redmine-API-Key", API_KEY)
                .GET()
                .build();
        try {
            HttpClient client = HttpClient.newHttpClient();
            HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

            JsonObject jsonObject = JsonParser.parseString(response.body()).getAsJsonObject();
            JsonObject userJson = jsonObject.get("user").getAsJsonObject();

            RedmineUser user = new RedmineUser();
            user.setId(userJson.get("id").getAsInt());
            user.setLogin(userJson.get("login").getAsString());
            user.setFirstname(userJson.get("firstname").getAsString());
            user.setLastname(userJson.get("lastname").getAsString());
            user.setMail(userJson.get("mail").getAsString());
            user.setKey(userJson.get("api_key").getAsString());
            return user;

            //return response.body();
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
            return null;
        }
    }


    public String uploadFile(byte[] fileContent, String fileName) {
        HttpClient client = HttpClient.newHttpClient();

        try {
            // Codificar el nombre del archivo
            String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8);

            // Construir la URI para la solicitud HTTP
            URI uri = URI.create(REDMINE_URL + "/uploads.json?filename=" + encodedFileName);

            // Crear la solicitud HTTP
            HttpRequest request = HttpRequest.newBuilder()
                    .uri(uri)
                    .header("Content-Type", "application/octet-stream")
                    .header("X-Redmine-API-Key", API_KEY)
                    .POST(HttpRequest.BodyPublishers.ofByteArray(fileContent))
                    .build();

            // Enviar la solicitud HTTP y recibir la respuesta
            HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

            // Procesar la respuesta
                if (response.statusCode() == 201) {
                JsonObject jsonResponse = JsonParser.parseString(response.body()).getAsJsonObject();
                return jsonResponse.get("upload").getAsJsonObject().get("token").getAsString();
            } else {
                System.err.println("Error en la subida del archivo: " + response.statusCode());
                System.err.println("Response body: " + response.body());
                return null;
            }
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
            return null;
        }
    }


    public String createIssue(Map<String, String> issueDetails, JsonArray uploads, String apiKey) {
       JsonOrder.IssueDetails issueDetail = new JsonOrder.IssueDetails(
               issueDetails.get("project_id"),
               issueDetails.get("subject"),
               issueDetails.get("description"),
               uploads,
               issueDetails.get("tracker_id")
               );

       JsonOrder order = new JsonOrder(issueDetail);

        String jsonPayload = GSON.toJson(order);

        try {
            RedmineUser user = (RedmineUser) VaadinService.getCurrentRequest().getWrappedSession().getAttribute("myaccount");

            HttpRequest request = HttpRequest.newBuilder()
                    .uri(URI.create(REDMINE_URL + "/issues.json"))
                    .header("Content-Type", "application/json")
                    .header("X-Redmine-API-Key", user.getKey())
                    .POST(HttpRequest.BodyPublishers.ofString(jsonPayload))
                    .build();

            HttpClient client = HttpClient.newHttpClient();
            HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

            return response.body();
        } catch (Exception e) {
            e.printStackTrace();
            return "Error: " + e.getMessage();
        }
    }


    public Map<String, TicketType> getTicketTypes() {
        Map<String, TicketType> ticketTypes = new HashMap<>();
        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(REDMINE_URL + "/trackers.json"))
                .header("Content-Type", "application/json")
                .header("X-Redmine-API-Key", API_KEY)
                .build();
        try {
            HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
            if (response.statusCode() == 200) {
                String responseBody = response.body();
                ticketTypes = parseTicketTypes(responseBody);
            } else {
                System.err.println("Error en la respuesta: " + response.statusCode());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ticketTypes;
    }


    private Map<String, TicketType> parseTicketTypes(String json) {
        Map<String, TicketType> ticketMap = new HashMap<>();
        JsonObject jsonObject = JsonParser.parseString(json).getAsJsonObject();
        jsonObject.getAsJsonArray("trackers").forEach(trackerElement -> {
            JsonObject tracker = trackerElement.getAsJsonObject();
            String name = tracker.get("name").getAsString();
            String id = tracker.get("id").getAsString();
            String description = tracker.has("description") && !tracker.get("description").isJsonNull() ? tracker.get("description").getAsString() : "Descripcion no disponible";
            ticketMap.put(name, new TicketType(id, description));
        });
        return ticketMap;
    }

    public static class TicketType {
        private final String id;
        private final String description;

        public TicketType(String id, String description) {
            this.id = id;
            this.description = description;
        }

        public String getId() {
            return id;
        }

        public String getDescription() {
            return description;
        }

        public String getTicketTypeDescription(String type, Map<String, TicketType> ticketTypes) {
            return ticketTypes.getOrDefault(type, new TicketType("N/A", "Descripción no disponible para este tipo de ticket")).getDescription();
        }
    }


    public static RedmineUser createRedmineUser(String username, String firstname, String lastname, String mail) {
        HttpClient client = HttpClient.newHttpClient();
        Map<String, Object> user = new HashMap<>();
        user.put("login", username);
        user.put("firstname", firstname);
        user.put("lastname", lastname);
        user.put("mail", mail);
        user.put("auth_source_id", "1");

        Map<String, Object> payload = new HashMap<>();
        payload.put("user", user);
        String jsonPayload = new Gson().toJson(payload);
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(REDMINE_URL + "/users.json"))
                .header("Content-Type", "application/json")
                .header("X-Redmine-API-Key", API_KEY)
                .POST(HttpRequest.BodyPublishers.ofString(jsonPayload))
                .build();

        try {
            HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

            JsonObject jsonObject = JsonParser.parseString(response.body()).getAsJsonObject();
            JsonObject userJson = jsonObject.get("user").getAsJsonObject();

            RedmineUser newUser = new RedmineUser();
            newUser.setId(userJson.get("id").getAsInt());
            newUser.setLogin(userJson.get("login").getAsString());
            newUser.setFirstname(userJson.get("firstname").getAsString());
            newUser.setLastname(userJson.get("lastname").getAsString());
            newUser.setMail(userJson.get("mail").getAsString());

            return newUser;

        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}
