"""The spoken-asset matcher.

Behaviour, not rendering: given words a technician would actually say, does the
right asset win, and does an unrelated sentence correctly match nothing?
"""

from __future__ import annotations

from dataclasses import dataclass

import pytest

from app.services.assets import MIN_SCORE, _significant, _tokenise, match_asset


@dataclass
class FakeAsset:
    id: int
    equipment_no: str
    description: str
    line: str | None = None


class FakeSession:
    """Stands in for a Session: match_asset only ever scans all assets."""

    def __init__(self, assets):
        self._assets = assets

    def scalars(self, _stmt):
        return self._assets


# Drawn from the real Britco equipment list.
ASSETS = [
    FakeAsset(1, "4CT-001-03-003", "BELLY CONVEYOR", "BELLY"),
    FakeAsset(2, "4CT-001-08-001", "BOX CONVEYOR #1", "BOX LINE"),
    FakeAsset(3, "2KL-001-03.1", "BLEED CONVEYOR", None),
    FakeAsset(4, "1186", "PRINTER SCALE #10", "FLOOR SCALE"),
    FakeAsset(5, "12350", "SCALE (RAIL) DYNAMIC", "KILL FLOOR"),
]


def _match(text):
    return match_asset(FakeSession(ASSETS), text)


class TestTokenising:
    def test_spoken_numbers_become_digits(self):
        assert "1" in _tokenise("conveyor one packaging")

    def test_filler_words_are_dropped(self):
        assert _significant(_tokenise("the motor is making a noise")) == {"motor"}


class TestMatching:
    def test_matches_the_asset_named_in_the_speech(self):
        result = _match("the belly conveyor outfeed bearing is shrieking")
        assert result is not None
        assert result[0].equipment_no == "4CT-001-03-003"

    def test_prefers_the_more_completely_named_asset(self):
        # "box conveyor 1" names BOX CONVEYOR #1 fully; BELLY CONVEYOR only
        # shares the word "conveyor".
        result = _match("box conveyor 1 is jammed again")
        assert result is not None
        assert result[0].description == "BOX CONVEYOR #1"

    def test_spoken_number_word_still_matches(self):
        result = _match("box conveyor number one has a broken roller")
        assert result is not None
        assert result[0].description == "BOX CONVEYOR #1"

    def test_equipment_number_spoken_aloud_is_decisive(self):
        result = _match("problem on 2KL-001-03.1 again")
        assert result is not None
        assert result[0].equipment_no == "2KL-001-03.1"

    def test_unrelated_speech_matches_nothing(self):
        assert _match("the coffee machine in the lunch room is broken") is None

    def test_empty_speech_matches_nothing(self):
        assert _match("") is None
        assert _match("   ") is None

    @pytest.mark.parametrize("text", ["the", "it is broken", "there is a problem"])
    def test_filler_only_speech_matches_nothing(self, text):
        assert _match(text) is None

    def test_a_match_always_clears_the_threshold(self):
        result = _match("belly conveyor")
        assert result is not None
        assert result[1] >= MIN_SCORE
