# -*- coding: utf-8 -*-
"""STEP 2 tests — parties, children, issues, proceedings, conflict screening,
intake questionnaire (strict / emergency fast-path).

Run just this step:
    odoo -d <db> -u activeblue_familylaw --test-enable \
         --test-tags familylaw_step2 --stop-after-init

All tests roll back inside a savepoint. No network calls.
"""

from datetime import date, timedelta

from dateutil.relativedelta import relativedelta

from odoo.tests.common import TransactionCase, new_test_user, tagged
from odoo.exceptions import UserError, ValidationError


@tagged("post_install", "-at_install", "familylaw", "familylaw_step2")
class TestStep2RelationsAndProceeding(TransactionCase):
    """Relation integrity, DOB constraint, initial proceeding creation."""

    @classmethod
    def setUpClass(cls):
        super().setUpClass()
        cls.partner_a = cls.env["res.partner"].create({"name": "Client A"})
        cls.partner_b = cls.env["res.partner"].create({"name": "Client B"})
        cls.attorney = new_test_user(
            cls.env,
            login="fl_atty2",
            name="Test Attorney 2",
            email="atty2@example.com",
            groups="base.group_user,activeblue_familylaw_v2.group_familylaw_attorney",
        )
        cls.paralegal = new_test_user(
            cls.env,
            login="fl_para2",
            name="Test Paralegal 2",
            email="para2@example.com",
            groups="base.group_user,activeblue_familylaw_v2.group_familylaw_user",
        )
        cls.Case = cls.env["familylaw.case"]

    def _make_case(self, case_type="dissolution_children", **kw):
        vals = {
            "name": "Test Matter",
            "client_id": self.partner_a.id,
            "case_type": case_type,
        }
        vals.update(kw)
        return self.Case.create(vals)

    # --- initial proceeding -------------------------------------------------
    def test_01_case_create_opens_initial_proceeding(self):
        case = self._make_case()
        self.assertEqual(len(case.proceeding_ids), 1)
        proc = case.proceeding_ids[0]
        self.assertEqual(proc.state, "open")

    def test_02_modification_case_creates_modification_proceeding(self):
        case = self._make_case(case_type="support_modification")
        proc = case.proceeding_ids[0]
        self.assertEqual(proc.proceeding_type, "modification")

    def test_03_original_case_creates_original_proceeding(self):
        case = self._make_case(case_type="dissolution_children")
        proc = case.proceeding_ids[0]
        self.assertEqual(proc.proceeding_type, "original")

    # --- multiple proceedings -----------------------------------------------
    def test_04_case_can_hold_multiple_proceedings(self):
        case = self._make_case()
        self.env["familylaw.proceeding"].create({
            "case_id": case.id,
            "name": "Second Proceeding",
            "proceeding_type": "modification",
        })
        self.assertEqual(len(case.proceeding_ids), 2)
        states = set(case.proceeding_ids.mapped("state"))
        self.assertEqual(states, {"open"})

    def test_05_proceeding_states_are_independent(self):
        case = self._make_case()
        extra = self.env["familylaw.proceeding"].create({
            "case_id": case.id,
            "name": "Extra",
            "proceeding_type": "enforcement",
        })
        extra.action_close_proceeding()
        self.assertEqual(extra.state, "closed")
        # Initial proceeding stays open
        initial = case.proceeding_ids.filtered(lambda p: p != extra)
        self.assertEqual(initial.state, "open")

    # --- parties ------------------------------------------------------------
    def test_06_party_links_to_case(self):
        case = self._make_case()
        party = self.env["familylaw.party"].create({
            "case_id": case.id,
            "name": "Jane Doe",
            "role": "opposing_party",
        })
        self.assertIn(party, case.party_ids)

    # --- children -----------------------------------------------------------
    def test_07_child_links_to_case(self):
        case = self._make_case()
        child = self.env["familylaw.child"].create({
            "case_id": case.id,
            "name": "Minor Child A",
            "date_of_birth": date.today() - timedelta(days=365 * 5),
        })
        self.assertIn(child, case.child_ids)

    def test_08_dob_future_rejected(self):
        case = self._make_case()
        with self.assertRaises(ValidationError):
            self.env["familylaw.child"].create({
                "case_id": case.id,
                "name": "Future Child",
                "date_of_birth": date.today() + timedelta(days=1),
            })

    def test_09_dob_over_25_rejected(self):
        case = self._make_case()
        implausible_dob = date.today().replace(year=date.today().year - 26)
        with self.assertRaises(ValidationError):
            self.env["familylaw.child"].create({
                "case_id": case.id,
                "name": "Old Child",
                "date_of_birth": implausible_dob,
            })

    def test_10_dob_valid_accepted(self):
        case = self._make_case()
        valid_dob = date.today() - relativedelta(years=8)
        child = self.env["familylaw.child"].create({
            "case_id": case.id,
            "name": "Young Child",
            "date_of_birth": valid_dob,
        })
        self.assertTrue(child.id)
        self.assertEqual(child.age, 8)

    # --- issues -------------------------------------------------------------
    def test_11_issue_links_to_case(self):
        case = self._make_case()
        issue = self.env["familylaw.issue"].create({
            "case_id": case.id,
            "issue_type": "child_support",
        })
        self.assertIn(issue, case.issue_ids)


@tagged("post_install", "-at_install", "familylaw", "familylaw_step2")
class TestStep2SearchByPerson(TransactionCase):
    """Find a case by party name, child name, and case_number."""

    @classmethod
    def setUpClass(cls):
        super().setUpClass()
        cls.partner = cls.env["res.partner"].create({"name": "Search Client"})
        cls.case = cls.env["familylaw.case"].create({
            "name": "Search Matter",
            "client_id": cls.partner.id,
            "case_type": "dissolution_children",
            "case_number": "2024-DR-999999",
        })
        cls.env["familylaw.party"].create({
            "case_id": cls.case.id,
            "name": "John Opposing",
            "role": "opposing_party",
        })
        cls.env["familylaw.child"].create({
            "case_id": cls.case.id,
            "name": "Little One",
            "date_of_birth": date.today() - timedelta(days=365 * 6),
        })

    def test_12_find_by_case_number(self):
        results = self.env["familylaw.case"].search(
            [("case_number", "=", "2024-DR-999999")]
        )
        self.assertIn(self.case, results)

    def test_13_find_by_party_name(self):
        results = self.env["familylaw.case"].search(
            [("party_ids.name", "ilike", "John Opposing")]
        )
        self.assertIn(self.case, results)

    def test_14_find_by_child_name(self):
        results = self.env["familylaw.case"].search(
            [("child_ids.name", "ilike", "Little One")]
        )
        self.assertIn(self.case, results)


@tagged("post_install", "-at_install", "familylaw", "familylaw_step2")
class TestStep2ConflictScreening(TransactionCase):
    """Conflict screening surfaces a past-client opposing party; does not auto-clear."""

    @classmethod
    def setUpClass(cls):
        super().setUpClass()
        cls.attorney = new_test_user(
            cls.env,
            login="fl_atty_conflict",
            name="Conflict Attorney",
            email="catty@example.com",
            groups="base.group_user,activeblue_familylaw_v2.group_familylaw_attorney",
        )
        # Case A: "Old Client" is the client
        cls.old_client = cls.env["res.partner"].create({"name": "Old Client"})
        cls.case_a = cls.env["familylaw.case"].create({
            "name": "Old Matter",
            "client_id": cls.old_client.id,
            "case_type": "dissolution_children",
        })

        # Case B: "Old Client" appears as an opposing party
        cls.new_client = cls.env["res.partner"].create({"name": "New Client"})
        cls.case_b = cls.env["familylaw.case"].create({
            "name": "New Matter",
            "client_id": cls.new_client.id,
            "case_type": "support_modification",
        })
        cls.env["familylaw.party"].create({
            "case_id": cls.case_b.id,
            "name": "Old Client",
            "role": "opposing_party",
        })

    def test_15_conflict_screening_finds_client_match(self):
        self.case_b.action_run_conflict_screening()
        hits = self.case_b.conflict_hit_ids
        self.assertTrue(hits, "Expected at least one conflict hit for 'Old Client'")

    def test_16_conflict_does_not_auto_clear(self):
        self.case_b.action_run_conflict_screening()
        self.assertFalse(
            self.case_b.conflict_check_cleared,
            "Conflict screening must never auto-clear the attorney gate.",
        )

    def test_17_conflict_hit_count_updates(self):
        self.case_b.action_run_conflict_screening()
        self.assertGreater(self.case_b.conflict_hit_count, 0)

    def test_18_engage_blocked_with_uncleared_conflict(self):
        """Ensure Step 1's engage gate still works after Step 2 additions."""
        with self.assertRaises(UserError):
            self.case_b.action_engage()

    def test_19_attorney_can_clear_conflict_after_reviewing_hits(self):
        self.case_b.action_run_conflict_screening()
        self.case_b.with_user(self.attorney).action_mark_conflict_cleared()
        self.assertTrue(self.case_b.conflict_check_cleared)

    def test_20_no_conflict_for_unrelated_opposing_party(self):
        unique_client = self.env["res.partner"].create({"name": "Completely Unique Person XYZ"})
        case_c = self.env["familylaw.case"].create({
            "name": "Unrelated Matter",
            "client_id": unique_client.id,
            "case_type": "paternity",
        })
        self.env["familylaw.party"].create({
            "case_id": case_c.id,
            "name": "Completely Unique Opposing XYZ",
            "role": "opposing_party",
        })
        case_c.action_run_conflict_screening()
        self.assertEqual(case_c.conflict_hit_count, 0)


@tagged("post_install", "-at_install", "familylaw", "familylaw_step2")
class TestStep2IntakeWizard(TransactionCase):
    """Intake questionnaire — strict path, emergency fast-path, triage, question capture."""

    @classmethod
    def setUpClass(cls):
        super().setUpClass()
        cls.Wizard = cls.env["familylaw.intake.wizard"]
        cls.partner = cls.env["res.partner"].create({"name": "Existing Client"})

    def _make_wizard(self, **vals):
        return self.Wizard.create(vals)

    # --- standard path strict validation ------------------------------------
    def test_21_standard_path_requires_matter_name(self):
        wiz = self._make_wizard(
            step="details",
            case_type="dissolution_children",
            county="miami_dade",
            caller_name="Caller X",
        )
        with self.assertRaises(UserError):
            wiz._validate_standard_details()

    def test_22_standard_path_requires_case_type(self):
        wiz = self._make_wizard(
            step="details",
            matter_name="Test Matter",
            county="miami_dade",
            caller_name="Caller X",
        )
        with self.assertRaises(UserError):
            wiz._validate_standard_details()

    def test_23_standard_path_requires_county(self):
        wiz = self._make_wizard(
            step="details",
            matter_name="Test Matter",
            case_type="dissolution_children",
            caller_name="Caller X",
            county=False,
        )
        with self.assertRaises(UserError):
            wiz._validate_standard_details()

    def test_24_standard_path_requires_client_or_caller_name(self):
        wiz = self._make_wizard(
            step="details",
            matter_name="Test Matter",
            case_type="dissolution_children",
            county="miami_dade",
        )
        with self.assertRaises(UserError):
            wiz._validate_standard_details()

    def test_25_standard_path_creates_case(self):
        wiz = self._make_wizard(
            caller_name="John Smith",
            case_type="dissolution_children",
            county="miami_dade",
        )
        wiz.matter_name = "In re Smith"
        wiz.client_partner_id = self.partner.id
        wiz._create_standard_case()
        case = wiz.created_case_id
        self.assertTrue(case)
        self.assertEqual(case.state, "intake")
        self.assertFalse(case.is_emergency)

    def test_26_standard_case_gets_initial_proceeding(self):
        wiz = self._make_wizard(
            caller_name="Jane Doe",
            case_type="support_modification",
            county="miami_dade",
        )
        wiz.matter_name = "In re Doe"
        wiz.client_partner_id = self.partner.id
        wiz._create_standard_case()
        case = wiz.created_case_id
        self.assertEqual(len(case.proceeding_ids), 1)
        self.assertEqual(case.proceeding_ids[0].proceeding_type, "modification")

    # --- triage sets case type ----------------------------------------------
    def test_27_triage_case_type_propagates_to_case(self):
        wiz = self._make_wizard(
            caller_name="Alice",
            case_type="paternity",
            county="miami_dade",
        )
        wiz.matter_name = "Paternity Matter"
        wiz.client_partner_id = self.partner.id
        wiz._create_standard_case()
        self.assertEqual(wiz.created_case_id.case_type, "paternity")

    # --- emergency fast-path ------------------------------------------------
    def test_28_emergency_path_creates_case_on_minimum_facts(self):
        wiz = self._make_wizard(
            caller_name="Emergency Caller",
            urgency_child_withheld=True,
        )
        self.assertTrue(wiz.is_emergency)
        wiz._create_emergency_case()
        case = wiz.created_case_id
        self.assertTrue(case, "Emergency case should be created")
        self.assertEqual(case.state, "intake")
        self.assertTrue(case.is_emergency)

    def test_29_emergency_flag_set_on_case(self):
        wiz = self._make_wizard(
            caller_name="Urgent Caller",
            urgency_violence=True,
        )
        wiz._create_emergency_case()
        self.assertTrue(wiz.created_case_id.is_emergency)

    def test_30_emergency_creates_partner_from_caller_name(self):
        wiz = self._make_wizard(
            caller_name="Brand New Person",
            urgency_removal_threat=True,
        )
        wiz._create_emergency_case()
        case = wiz.created_case_id
        self.assertEqual(case.client_id.name, "Brand New Person")

    def test_31_emergency_urgency_notes_captured(self):
        wiz = self._make_wizard(
            caller_name="Person In Danger",
            urgency_violence=True,
            urgency_description="Ex-partner threatened caller this morning.",
        )
        wiz._create_emergency_case()
        self.assertIn("Ex-partner", wiz.created_case_id.urgency_notes)

    # --- caller concern captured as attorney question, never answered -------
    def test_32_caller_concern_logged_on_case_not_answered(self):
        wiz = self._make_wizard(
            caller_name="Concerned Caller",
            case_type="support_modification",
            county="miami_dade",
            caller_concern="Do I have a good case?",
        )
        wiz.matter_name = "Concern Matter"
        wiz.client_partner_id = self.partner.id
        wiz._create_standard_case()
        case = wiz.created_case_id
        chatter_bodies = " ".join(
            msg.body for msg in case.message_ids if msg.body
        )
        self.assertIn("Do I have a good case?", chatter_bodies)
        self.assertIn("attorney", chatter_bodies.lower())

    def test_33_emergency_concern_also_logged(self):
        wiz = self._make_wizard(
            caller_name="Emergency Concern",
            urgency_child_withheld=True,
            caller_concern="Am I going to win this?",
        )
        wiz._create_emergency_case()
        case = wiz.created_case_id
        chatter_bodies = " ".join(msg.body for msg in case.message_ids if msg.body)
        self.assertIn("Am I going to win this?", chatter_bodies)

    # --- opposing party + conflict screening via wizard ---------------------
    def test_34_wizard_adds_opposing_party_and_screens(self):
        # Set up an existing case with this person as a client
        existing_client = self.env["res.partner"].create({"name": "Opposing Client Match"})
        self.env["familylaw.case"].create({
            "name": "Existing Matter",
            "client_id": existing_client.id,
            "case_type": "dissolution_children",
        })

        wiz = self._make_wizard(
            caller_name="New Caller",
            case_type="enforcement",
            county="miami_dade",
            opposing_party_name="Opposing Client Match",
        )
        wiz.matter_name = "Enforcement Matter"
        wiz.client_partner_id = self.partner.id
        wiz._create_standard_case()
        case = wiz.created_case_id
        # Should have at least one party record
        self.assertTrue(case.party_ids)
        # Should have at least one conflict hit
        self.assertGreater(case.conflict_hit_count, 0)
