package com.example.equipment;

import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

import com.example.equipment.model.Equipment;
import com.example.equipment.repository.EquipmentRepository;
import com.example.equipment.service.EquipmentService;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

/**
 * 到達基準の受け入れテスト。
 *
 * 配布時点では2件が失敗する。演習Bで不具合を2件修正すると、すべて成功する。
 * 業務ルールの正本は kadaiB/README.md の「業務ルール（5項目）」。
 */
@SpringBootTest
@DisplayName("備品管理システムの受け入れテスト")
class EquipmentAcceptanceTest {

    /** 初期データ（data.sql）の件数 */
    private static final int INITIAL_DATA_COUNT = 20;

    /** 要補充の境界値。業務ルール1で「5個以下」と決めている */
    private static final int LOW_STOCK_BOUNDARY = 5;

    @Autowired
    private EquipmentService equipmentService;

    @Autowired
    private EquipmentRepository equipmentRepository;

    /**
     * 業務ルール5の確認。
     * 備考が未設定（NULL）の備品でも、詳細画面用の備考欄が作れることを確かめる。
     * 修正前は NullPointerException が発生し、画面が500エラーになる。
     */
    @Test
    @DisplayName("備考が未設定の備品でも詳細の備考欄を組み立てられる")
    void buildsNoteSummaryEvenWhenNoteIsMissing() {
        Equipment noteMissing = findByCode("EQ-0003");

        String summary = assertDoesNotThrow(
                () -> equipmentService.buildNoteSummary(noteMissing),
                "備考が未設定の備品で例外が発生しています。詳細画面が500エラーになります");

        assertNotNull(summary, "備考が未設定のときも、画面に出せる文字列を返してください");
    }

    /**
     * 業務ルール1の確認。
     * 在庫がちょうど5個の備品が「要補充」と判定されることを確かめる。
     * 修正前は5個ちょうどが判定から漏れ、要補充の表示が出ない。
     */
    @Test
    @DisplayName("在庫が5個ちょうどの備品は要補充と判定される")
    void treatsStockOfExactlyFiveAsLowStock() {
        Equipment exactlyFive = findByCode("EQ-0004");

        assertEquals(LOW_STOCK_BOUNDARY, exactlyFive.getStock().intValue(),
                "EQ-0004 の在庫が5個ではありません。data.sql が書き換えられていないか確認してください");
        assertTrue(equipmentService.isLowStock(exactlyFive),
                "在庫5個ちょうどの備品が要補充になっていません。業務ルール1は「5個以下」です");
    }

    /**
     * 上の判定が逆に振れていないことの確認。
     * 在庫が6個の備品は要補充にならない。
     */
    @Test
    @DisplayName("在庫が6個の備品は要補充と判定されない")
    void doesNotTreatStockOfSixAsLowStock() {
        Equipment six = findByCode("EQ-0011");

        assertEquals(6, six.getStock().intValue(),
                "EQ-0011 の在庫が6個ではありません。data.sql が書き換えられていないか確認してください");
        assertFalse(equipmentService.isLowStock(six),
                "在庫6個の備品まで要補充になっています。境界の向きを確認してください");
    }

    /**
     * 備考の要約仕様の確認。
     * 20文字を超える備考は、先頭20文字に「...」を付けた形に短くする。
     * 備考未設定の修正を入れたあとも、この動きが変わっていないことを見る。
     */
    @Test
    @DisplayName("20文字を超える備考は先頭20文字に短縮される")
    void truncatesLongNoteToTwentyCharacters() {
        Equipment longNote = findByCode("EQ-0001");
        String note = longNote.getNote();

        assertNotNull(note, "EQ-0001 の備考が消えています。data.sql を確認してください");
        assertTrue(note.length() > 20, "EQ-0001 の備考が20文字以下になっています。data.sql を確認してください");

        String summary = equipmentService.buildNoteSummary(longNote);

        assertEquals(note.substring(0, 20) + "...", summary,
                "長い備考の短縮結果が変わっています");
    }

    /**
     * 初期データの件数確認。
     * data.sql を書き換えて症状を消していないことを見る。
     */
    @Test
    @DisplayName("初期データの備品が20件そろっている")
    void loadsTwentyEquipmentsFromInitialData() {
        assertEquals(INITIAL_DATA_COUNT, equipmentRepository.findAll().size(),
                "初期データの件数が変わっています。data.sql は書き換えないでください");
    }

    /** 備品コードで初期データを1件取り出す（テスト用の小さな補助メソッド） */
    private Equipment findByCode(String code) {
        return equipmentRepository.findAll().stream()
                .filter(equipment -> code.equals(equipment.getCode()))
                .findFirst()
                .orElseThrow(() -> new IllegalStateException(
                        "初期データに " + code + " が見つかりません。data.sql を確認してください"));
    }
}
