"""Integration tests for ECMDmsAsync.copy().

Requires a running ECM server at localhost:4000.

Note on the test schema:
    ALL test object types (``Unittest_DMS_Document``, ``Unittest_DMS_Register``,
    ``Unittest_DMS_Subregister``) have ``Name`` defined as a key field on the server.
    Plain copies are rejected by the server for objects with key fields.

    Document copies use ``link_document=True``, which creates a second filing location
    for the same index record instead of duplicating the data.  There is no equivalent
    option for registers, so the register-copy tests are skipped on this server.

Object hierarchy used in every test:
    folder (Unittest_DMS)
      register1 (Unittest_DMS_Register)          ← register copy source (skipped)
        subregister1 (Unittest_DMS_Subregister)  ← empty
      register2 (Unittest_DMS_Register)
        subregister2 (Unittest_DMS_Subregister)  ← document link target
        subregister_src (Unittest_DMS_Subregister) ← document source / primary location
      doc (Unittest_DMS_Document in subregister_src)
"""

import os
import unittest
from uuid import UUID

from ecmind_blue_client.ecm import ECM
from ecmind_blue_client.pool import AsyncPoolClient
from ecmind_blue_client.rpc._job_request_file import JobRequestFileFromPath
from tests.models.Unittest_DMS import (
    Unittest_DMS,
    Unittest_DMS_Document,
    Unittest_DMS_Register,
    Unittest_DMS_Subregister,
)
from tests._test_settings import ECM_SERVERS, ECM_USERNAME, ECM_PASSWORD


class TestEcmDmsCopyAsync(unittest.IsolatedAsyncioTestCase):
    """Integration tests for ECMDmsAsync.copy() — requires localhost:4000."""

    def setUp(self):
        self.client = AsyncPoolClient(servers=ECM_SERVERS, username=ECM_USERNAME, password=ECM_PASSWORD)
        self.ecm = ECM(self.client)

    def tearDown(self):
        del self.client

    async def asyncSetUp(self):
        uuid = UUID(bytes=os.urandom(16), version=4)

        folder_id, folder_type_id = await self.ecm.dms.insert(Unittest_DMS(Name=f"CopyTest_{uuid}"))
        self.folder_id = folder_id
        self.folder_type_id = folder_type_id

        self.register1 = await self.ecm.dms.insert_and_get(
            Unittest_DMS_Register(Name=f"CopyReg1_{uuid}"),
            folder_id=folder_id,
        )
        self.subregister1 = await self.ecm.dms.insert_and_get(
            Unittest_DMS_Subregister(Name=f"CopySub1_{uuid}"),
            folder_id=folder_id,
            register_id=self.register1.system.id,
        )
        self.register2 = await self.ecm.dms.insert_and_get(
            Unittest_DMS_Register(Name=f"CopyReg2_{uuid}"),
            folder_id=folder_id,
        )
        self.subregister2 = await self.ecm.dms.insert_and_get(
            Unittest_DMS_Subregister(Name=f"CopySub2_{uuid}"),
            folder_id=folder_id,
            register_id=self.register2.system.id,
        )
        self.subregister_src = await self.ecm.dms.insert_and_get(
            Unittest_DMS_Subregister(Name=f"CopySubSrc_{uuid}"),
            folder_id=folder_id,
            register_id=self.register2.system.id,
        )
        self.doc = await self.ecm.dms.insert_and_get(
            Unittest_DMS_Document(Name=f"CopyDoc_{uuid}"),
            files=[JobRequestFileFromPath("README.md")],
            folder_id=folder_id,
            register_id=self.subregister_src.system.id,
        )
        self.subregister2_type_id = await self.ecm.dms.get_object_type_by_id(self.subregister2.system.id)

    async def asyncTearDown(self):
        folder = await self.ecm.dms.get(Unittest_DMS, self.folder_id, self.folder_type_id)
        await self.ecm.dms.delete(folder, hard_delete=True, delete_cascading=True)

    async def _count_in_register(self, model_class, register_id: int) -> int:
        return len(
            await self.ecm.dms.select(model_class)
            .where(model_class["SDREG_ID"] == register_id)
            .execute()
        )

    async def test_copy_document_as_link_via_model(self):
        await self.ecm.dms.copy(
            self.doc,
            folder_id=self.folder_id,
            register_id=self.subregister2.system.id,
            register_type=self.subregister2_type_id,
            link_document=True,
        )

        self.assertEqual(await self._count_in_register(Unittest_DMS_Document, self.subregister2.system.id), 1)

    async def test_copy_document_link_by_plain_id_and_type_id(self):
        doc_type_id = await self.ecm.dms.get_object_type_by_id(self.doc.system.id)
        await self.ecm.dms.copy(
            self.doc.system.id,
            doc_type_id,
            folder_id=self.folder_id,
            register_id=self.subregister2.system.id,
            register_type=self.subregister2_type_id,
            link_document=True,
        )

        self.assertEqual(await self._count_in_register(Unittest_DMS_Document, self.subregister2.system.id), 1)

    async def test_copy_document_link_by_plain_id_and_internal_name(self):
        await self.ecm.dms.copy(
            self.doc.system.id,
            Unittest_DMS_Document._internal_name_,
            folder_id=self.folder_id,
            register_id=self.subregister2.system.id,
            register_type=self.subregister2_type_id,
            link_document=True,
        )

        self.assertEqual(await self._count_in_register(Unittest_DMS_Document, self.subregister2.system.id), 1)

    @unittest.skip("Unittest_DMS_Register has Name as a key field; server rejects plain register copies")
    async def test_copy_register_without_cascading(self):
        await self.ecm.dms.copy(
            self.register1,
            folder_id=self.folder_id,
            register_id=-1,
            register_type=-1,
        )

        registers = await self.ecm.dms.select(Unittest_DMS_Register).where(
            Unittest_DMS_Register["SDSTA_ID"] == self.folder_id
        ).execute()
        self.assertGreaterEqual(len(registers), 2)

    @unittest.skip("Unittest_DMS_Register has Name as a key field; server rejects plain register copies")
    async def test_copy_register_cascading(self):
        await self.ecm.dms.copy(
            self.register1,
            folder_id=self.folder_id,
            register_id=-1,
            register_type=-1,
            copy_cascading=True,
        )

        registers = await self.ecm.dms.select(Unittest_DMS_Register).where(
            Unittest_DMS_Register["SDSTA_ID"] == self.folder_id
        ).execute()
        self.assertGreaterEqual(len(registers), 2)

        subregisters = await self.ecm.dms.select(Unittest_DMS_Subregister).where(
            Unittest_DMS_Subregister["SDSTA_ID"] == self.folder_id
        ).execute()
        self.assertGreaterEqual(len(subregisters), 2)

    async def test_copy_raises_when_plain_id_without_object_type(self):
        with self.assertRaises(ValueError):
            await self.ecm.dms.copy(
                self.doc.system.id,
                folder_id=self.folder_id,
                register_id=self.subregister2.system.id,
            )

    async def test_copy_raises_when_model_has_no_id(self):
        doc_without_id = Unittest_DMS_Document(Name="no_id")
        with self.assertRaises(ValueError):
            await self.ecm.dms.copy(doc_without_id, folder_id=self.folder_id)
