package com.tutego.ch_06.read;

import jakarta.persistence.EntityManager;
import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;
import org.springframework.shell.table.BorderStyle;
import org.springframework.shell.table.TableBuilder;
import org.springframework.shell.table.TableModelBuilder;

import java.time.LocalDate;
import java.util.List;
import java.util.Optional;

@ShellComponent
public class ReadProfileShellComponent {

    private static final int PAGE_SIZE = 10;
    private final EntityManager entityManager;

    public ReadProfileShellComponent(EntityManager entityManager) {
        this.entityManager = entityManager;
    }

    @ShellMethod("Display profile")
    public String findProfile(long id) {
        return formatProfilesAsTable(
                Optional.ofNullable(entityManager.find(Profile.class, id))
                        .stream()
                        .toList()
        );
    }

    @ShellMethod("Display all profiles")
    public String findProfiles(int page) {
        return formatProfilesAsTable(
                entityManager
                        .createNamedQuery("Profile.findAll", Profile.class)
                        .setFirstResult(page * PAGE_SIZE) // set the position of the first result to retrieve
                        .setMaxResults(PAGE_SIZE) // set the maximum number of results to retrieve
                        .getResultList()
        );
    }

    @ShellMethod("Display profiles by SOUNDEX")
    public String findProfilesBySound(String nickname) {
        return formatProfilesAsTable(
                // Jakarta Persistence provider recognizes from the name that it’s a named native query
                entityManager.createNamedQuery("Profile.findFuzzyNickname", Profile.class)
                        .setParameter(1,nickname)
                        .getResultList()
        );
    }

    @ShellMethod("Display profiles with vowels")
    public String findProfilesWithVowels() {
        return formatShortProfilesAsTable(
                // Jakarta Persistence provider recognizes from the name that it’s a named native query
                entityManager.createNamedQuery("Profile.containsTwoVowelsNickname", ShortProfile.class)
                        .getResultList()
        );
    }

    @ShellMethod("Display short profiles")
    public String findShortProfiles() {
        return formatShortProfilesAsTable(
                entityManager // constructor expressions JPQL, EntityMager is not required to manage any beans
                        .createQuery("SELECT new com.tutego.ch_06.jakartaPersistence.ShortProfile(p.nickname, p.maneLength) FROM Profile AS p", ShortProfile.class)
                        .getResultList()
        );
    }

    @ShellMethod("Display profiles by birth date")
    public String findProfilesByBirthDate(LocalDate birthDate) {
        return formatProfilesAsTable(
                entityManager
                        .createQuery("SELECT p FROM Profile AS p WHERE p.birthdate BETWEEN :birthdate AND LOCAL DATE ORDER BY p.birthdate", Profile.class)
                        .setParameter("birthdate", birthDate)
                        .getResultList()
        );
    }

    private static String formatShortProfilesAsTable(List<ShortProfile> profiles) {
        var tableModel = profiles.stream().reduce(
                new TableModelBuilder<String>().addRow().addValue("Nickname").addValue("Mane length"),
                (builder, profile) ->
                        builder.addRow().addValue(profile.nickname()).addValue(String.valueOf(profile.maneLength())),
                (a, b) -> a
        );

        return new TableBuilder(tableModel.build())
                .addFullBorder(BorderStyle.fancy_light)
                .build()
                .render(100);
    }

    private static String formatProfilesAsTable(List<Profile> profiles) {
        var tableModel = profiles.stream().reduce(
                new TableModelBuilder<String>()
                        .addRow()
                        .addValue("ID")
                        .addValue("Mane length")
                        .addValue("Nickname")
                        .addValue("Birthdate"),
                (builder, profile) ->
                        builder
                                .addRow()
                                .addValue(String.valueOf(profile.getId()))
                                .addValue(String.valueOf(profile.getManeLength()))
                                .addValue(profile.getNickname())
                                .addValue(profile.getBirthdate().toString()),
                (b1, b2) -> b1
        );

        return new TableBuilder(tableModel.build())
                .addFullBorder(BorderStyle.fancy_light)
                .build()
                .render(100);
    }
}