"""
Script Name     : VisureTestProcess.py
Description     : Implements Visure's tests process
Author          : Visure Solutions, Inc.
Company         : Visure Solutions, Inc.
Creation Date   : February 14, 2024
Last Modified   : February 14, 2024
Version         : 1.0 Beta
"""


import datetime
import Visure


# Visure test process

PROJECTS            = []                    # Projects - Leave empty to apply to any project
SPECIFICATIONS      = ["Runs"]              # Specifications - Leave empty to apply to any specification

TEST_STATUS         = "Test Status"         # Status attribute
TEST_STATUS_PENDING = "Pending"             # 'Pending' status
TEST_STATUS_FAILED  = "Fail"                # 'Failed' status
TEST_STATUS_PASSED  = "Pass"                # 'Passed' status
TEST_STATUS_BLOCKED = "Blocked"             # 'Blocked' status
TEST_STATUS_NA      = "N/A"                 # 'N/A' status


# Tests
def Visure_afterSetEnumValue(bl, lElementID, lAttributeID, lNewValueID, lEnumValueID):
    try:
        # Check the project
        if len(PROJECTS) > 0 and bl.GetProjectName() not in PROJECTS:
            return True

        # The item
        if not bl.ExistsElement(lElementID, 0):
            return True

        item = bl.item(lElementID)

        # Only perform following operations if item has parent
        if item.has_parent:
            # Check the specification
            if len(SPECIFICATIONS) > 0:
                belongToAnySpecification = False

                for speName in SPECIFICATIONS:
                    spe = bl.specification(speName)
                    if item.belongsToSpecification(spe.id):
                        belongToAnySpecification = True
                        break

                if not belongToAnySpecification:
                    return True

            # Check the status attribute
            if len(TEST_STATUS) > 0 and bl.GetAttributeID(TEST_STATUS) != -1:
                # Due date attribute
                statusAttr = bl.attribute(TEST_STATUS)

                # Check the id
                if statusAttr.id != lAttributeID:
                    return True

                # Look for status value ids
                testStatusPendingId = 0
                testStatusFailedId  = 0
                testStatusPassedId  = 0
                testStatusBlockedId = 0
                testStatusNAId      = 0

                statusType = statusAttr.type

                for status_values in statusType.enum_values:
                    if status_values.name.casefold() == TEST_STATUS_PENDING.casefold():
                        testStatusPendingId = status_values.id
                    elif status_values.name.casefold() == TEST_STATUS_FAILED.casefold():
                        testStatusFailedId = status_values.id
                    elif status_values.name.casefold() == TEST_STATUS_PASSED.casefold():
                        testStatusPassedId = status_values.id
                    elif status_values.name.casefold() == TEST_STATUS_BLOCKED.casefold():
                        testStatusBlockedId = status_values.id
                    elif status_values.name.casefold() == TEST_STATUS_NA.casefold():
                        testStatusNAId = status_values.id

                if testStatusPendingId == 0:
                    raise Exception(f"Cannot find id for value '{TEST_STATUS_PENDING}'")
                elif testStatusFailedId == 0:
                    raise Exception(f"Cannot find id for value '{TEST_STATUS_FAILED}'")
                elif testStatusPassedId == 0:
                    raise Exception(f"Cannot find id for value '{TEST_STATUS_PASSED}'")
                elif testStatusBlockedId == 0:
                    raise Exception(f"Cannot find id for value '{TEST_STATUS_BLOCKED}'")
                elif testStatusNAId == 0:
                    raise Exception(f"Cannot find id for value '{TEST_STATUS_NA}'")

                # Check new status
                if lEnumValueID in [testStatusPendingId,testStatusFailedId,testStatusPassedId,testStatusBlockedId,testStatusNAId]:
                    # Get the parent
                    parent = item.parent

                    if parent.is_checked_out_by_me and parent.has_attribute(lAttributeID):
                        if lEnumValueID == testStatusPendingId:
                            anyFailedOrBlocked = False

                            for child in parent.children:
                                if not anyFailedOrBlocked and child.id != lElementID and child.has_attribute(lAttributeID) and len(child.values(lAttributeID)) == 1:
                                    anyFailedOrBlocked = child.value(lAttributeID) == testStatusFailedId or child.value(lAttributeID) == testStatusBlockedId

                            if anyFailedOrBlocked:
                                parent.setValue(lAttributeID, testStatusFailedId)
                            else:
                                parent.setValue(lAttributeID, testStatusPendingId)
                        elif lEnumValueID == testStatusFailedId or lEnumValueID == testStatusBlockedId:
                            # Set parent status to failed
                            parent.setValue(lAttributeID, testStatusFailedId)
                        elif lEnumValueID == testStatusPassedId or lEnumValueID == testStatusNAId:
                            allSiblingsHavePassed = True
                            anySiblingFailedOrBlocked = False
                            anySiblingPending = False

                            for child in parent.children:
                                if allSiblingsHavePassed and child.id != lElementID and child.has_attribute(lAttributeID) and len(child.values(lAttributeID)) == 1:
                                    allSiblingsHavePassed = child.value(lAttributeID) == testStatusPassedId or child.value(lAttributeID) == testStatusNAId
                                if not anySiblingFailedOrBlocked and child.id != lElementID and child.has_attribute(lAttributeID) and len(child.values(lAttributeID)) == 1:
                                    anySiblingFailedOrBlocked = child.value(lAttributeID) == testStatusFailedId or child.value(lAttributeID) == testStatusBlockedId
                                if not anySiblingPending and child.id != lElementID and child.has_attribute(lAttributeID) and len(child.values(lAttributeID)) == 1:
                                    anySiblingPending = child.value(lAttributeID) == testStatusPendingId

                            if allSiblingsHavePassed:
                                parent.setValue(lAttributeID, testStatusPassedId)
                            elif anySiblingPending and not anySiblingFailedOrBlocked:
                                parent.setValue(lAttributeID, testStatusPendingId)
            else:
                bl.GetLastError()   # Resets error text

    except Exception as e:
        bl.Trace_WARNING(str(e))
        return str(e)

    return True
