package org.raddatz.familienarchiv.user;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import org.raddatz.familienarchiv.user.AppUser;
import org.springframework.context.annotation.DependsOn;
import org.raddatz.familienarchiv.document.Document;
import org.raddatz.familienarchiv.document.DocumentStatus;
import org.raddatz.familienarchiv.person.Person;
import org.raddatz.familienarchiv.tag.Tag;
import org.raddatz.familienarchiv.user.UserGroup;
import org.raddatz.familienarchiv.user.AppUserRepository;
import org.raddatz.familienarchiv.document.DocumentRepository;
import org.raddatz.familienarchiv.person.PersonRepository;
import org.raddatz.familienarchiv.tag.TagRepository;
import org.raddatz.familienarchiv.user.UserGroupRepository;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.core.env.Environment;
import org.springframework.security.crypto.password.PasswordEncoder;

import java.time.LocalDate;
import java.util.Set;

@Configuration
@RequiredArgsConstructor
@Slf4j
@DependsOn("flyway")
public class UserDataInitializer {

    static final String DEFAULT_ADMIN_EMAIL = "admin@familienarchiv.local";
    static final String DEFAULT_ADMIN_PASSWORD = "admin123";

    @Value("${app.admin.email:" + DEFAULT_ADMIN_EMAIL + "}")
    private String adminEmail;

    @Value("${app.admin.password:" + DEFAULT_ADMIN_PASSWORD + "}")
    private String adminPassword;

    private final AppUserRepository userRepository;
    private final UserGroupRepository groupRepository;
    private final Environment environment;

    @Bean
    public CommandLineRunner initAdminUser(PasswordEncoder passwordEncoder) {
        return args -> {
            if (userRepository.findByEmail(adminEmail).isEmpty()) {
                // Fail-closed in production: refuse to seed with the well-known
                // defaults. Otherwise an operator who forgets APP_ADMIN_USERNAME
                // / APP_ADMIN_PASSWORD locks production to admin@…/admin123 PERMANENTLY
                // (UserDataInitializer only seeds when the row is missing — see #513).
                // Allowed in dev/test/e2e because those run without secrets configured.
                boolean isLocalProfile = environment.matchesProfiles("dev", "test", "e2e");
                if (!isLocalProfile
                    && (DEFAULT_ADMIN_EMAIL.equals(adminEmail)
                        || DEFAULT_ADMIN_PASSWORD.equals(adminPassword))) {
                    throw new IllegalStateException(
                        "Refusing to seed admin user with default credentials outside "
                        + "the dev/test/e2e profiles. Set APP_ADMIN_USERNAME and "
                        + "APP_ADMIN_PASSWORD to non-default values before first boot — "
                        + "this lock-in is permanent."
                    );
                }
                log.info("Kein Admin-User '{}' gefunden. Erstelle Default-Admin...", adminEmail);

                // Reuse the Administrators group if it already exists (e.g. a
                // previous boot seeded the group but failed before creating
                // the admin user, or the operator deleted just the user row
                // to retry the seed with a new email). Blind-INSERTing would
                // violate user_groups_name_key and abort the context. See #518.
                UserGroup adminGroup = groupRepository.findByName("Administrators")
                        .orElseGet(() -> groupRepository.save(UserGroup.builder()
                                .name("Administrators")
                                .permissions(Set.of("ADMIN", "READ_ALL", "WRITE_ALL", "ANNOTATE_ALL", "ADMIN_USER", "ADMIN_TAG", "ADMIN_PERMISSION"))
                                .build()));

                AppUser admin = AppUser.builder()
                        .email(adminEmail)
                        .password(passwordEncoder.encode(adminPassword))
                        .groups(Set.of(adminGroup))
                        .build();
                userRepository.save(admin);

                log.info("Default Admin erstellt: Email='{}'", adminEmail);
            }
        };
    }


    /**
     * Deterministic seed data for E2E tests.
     *
     * Activated only with --spring.profiles.active=e2e (local and CI).
     * Idempotent: skips seeding if persons already exist (e.g. on restart).
     *
     * Persons, tags, and documents are hardcoded so E2E assertions are stable
     * across every run — no random names or dates.
     */
    @Bean
    @Profile("e2e")
    public CommandLineRunner initE2EData(PersonRepository personRepo,
            DocumentRepository docRepo,
            TagRepository tagRepo,
            PasswordEncoder passwordEncoder) {
        return args -> {
            userRepository.findByEmail(adminEmail).ifPresent(admin -> {
                admin.setPassword(passwordEncoder.encode(adminPassword));
                userRepository.save(admin);
                log.info("E2E seed: Admin-Passwort auf konfigurierten Wert zurückgesetzt.");
            });

            if (userRepository.findByEmail("reader@familyarchive.local").isEmpty()) {
                log.info("E2E seed: Erstelle 'reader'-Testbenutzer...");
                UserGroup leserGroup = groupRepository.findByName("Leser").orElseGet(() ->
                        groupRepository.save(UserGroup.builder()
                                .name("Leser")
                                .permissions(Set.of("READ_ALL"))
                                .build()));
                userRepository.save(AppUser.builder()
                        .email("reader@familyarchive.local")
                        .password(passwordEncoder.encode("reader123"))
                        .groups(Set.of(leserGroup))
                        .build());
                log.info("E2E seed: 'reader'-Testbenutzer erstellt.");
            }

            if (userRepository.findByEmail("reset@familyarchive.local").isEmpty()) {
                log.info("E2E seed: Erstelle 'reset'-Testbenutzer...");
                UserGroup leserGroup = groupRepository.findByName("Leser").orElseGet(() ->
                        groupRepository.save(UserGroup.builder()
                                .name("Leser")
                                .permissions(Set.of("READ_ALL"))
                                .build()));
                userRepository.save(AppUser.builder()
                        .email("reset@familyarchive.local")
                        .password(passwordEncoder.encode("reset123"))
                        .groups(Set.of(leserGroup))
                        .build());
                log.info("E2E seed: 'reset'-Testbenutzer erstellt.");
            }

            if (personRepo.count() > 0) {
                log.info("E2E seed: Personendaten bereits vorhanden, überspringe Dokument-Seed.");
                return;
            }

            log.info("E2E seed: Erstelle deterministische Testdaten...");

            // ── Persons ──────────────────────────────────────────────────────
            Person hans = personRepo.save(Person.builder()
                    .firstName("Hans").lastName("Müller").build());
            Person anna = personRepo.save(Person.builder()
                    .firstName("Anna").lastName("Schmidt").build());
            Person otto = personRepo.save(Person.builder()
                    .firstName("Otto").lastName("Fischer").build());
            Person maria = personRepo.save(Person.builder()
                    .firstName("Maria").lastName("Weber").build());

            // ── Tags ─────────────────────────────────────────────────────────
            Tag tagFamilie = tagRepo.save(Tag.builder().name("Familie").build());
            Tag tagKrieg   = tagRepo.save(Tag.builder().name("Krieg").build());
            Tag tagUrlaub  = tagRepo.save(Tag.builder().name("Urlaub").build());

            // ── Documents ────────────────────────────────────────────────────
            docRepo.save(Document.builder()
                    .title("Geburtsurkunde Hans Müller")
                    .originalFilename("geburtsurkunde_hans.pdf")
                    .status(DocumentStatus.UPLOADED)
                    .documentDate(LocalDate.of(1923, 4, 12))
                    .location("Berlin")
                    .sender(hans)
                    .receivers(Set.of(anna))
                    .tags(Set.of(tagFamilie))
                    .transcription("Hiermit wird beurkundet, dass Hans Müller am 12. April 1923 in Berlin geboren wurde.")
                    .build());

            docRepo.save(Document.builder()
                    .title("Brief aus dem Krieg")
                    .originalFilename("brief_krieg_1944.pdf")
                    .status(DocumentStatus.TRANSCRIBED)
                    .documentDate(LocalDate.of(1944, 6, 6))
                    .location("Normandie")
                    .sender(otto)
                    .receivers(Set.of(anna, maria))
                    .tags(Set.of(tagKrieg, tagFamilie))
                    .transcription("Liebe Anna, ich schreibe dir aus der Front. Es geht mir den Umständen entsprechend gut.")
                    .build());

            docRepo.save(Document.builder()
                    .title("Urlaubspostkarte Ostsee")
                    .originalFilename("postkarte_1965.jpg")
                    .status(DocumentStatus.PLACEHOLDER)
                    .documentDate(LocalDate.of(1965, 8, 3))
                    .location("Rügen")
                    .sender(anna)
                    .receivers(Set.of(hans))
                    .tags(Set.of(tagUrlaub))
                    .build());

            docRepo.save(Document.builder()
                    .title("Unbekanntes Dokument")
                    .originalFilename("unbekannt.pdf")
                    .status(DocumentStatus.PLACEHOLDER)
                    .documentDate(LocalDate.of(1950, 1, 1))
                    .location("München")
                    .receivers(Set.of(maria))
                    .build());

            docRepo.save(Document.builder()
                    .title("Scan ohne Titel")
                    .originalFilename("scan_ohne_titel.pdf")
                    .status(DocumentStatus.UPLOADED)
                    .documentDate(LocalDate.of(1978, 11, 20))
                    .location("Hamburg")
                    .sender(maria)
                    .receivers(Set.of(otto))
                    .build());

            log.info("E2E seed: {} Personen, {} Tags, {} Dokumente, {} Benutzer erstellt.",
                    personRepo.count(), tagRepo.count(), docRepo.count(), userRepository.count());
        };
    }
}
