"""
Item Type Definition and GL Account Mapping System

This module defines the complete item type system for purchases,
mapping each item type to its appropriate GL accounts for standard
accounting treatments.
"""
import uuid
from enum import Enum
from sqlalchemy import Column, String, ForeignKey, Text, Numeric, Boolean
from sqlalchemy.orm import relationship
from app.models.base import BaseModel


class ItemTypeEnum(str, Enum):
    """Enumeration of purchase item types with GL account mappings"""

    # Inventory Items
    INVENTORY = "inventory"  # Resale goods, raw materials
    FINISHED_GOODS = "finished_goods"  # Manufactured goods
    COMPONENTS = "components"  # Component parts for assembly
    RAW_MATERIALS = "raw_materials"  # Raw materials for manufacturing

    # Fixed Assets
    FIXED_ASSET_BUILDING = "fixed_asset_building"  # Buildings and structures (depreciated)
    FIXED_ASSET_MACHINERY = "fixed_asset_machinery"  # Machinery and equipment (depreciated)
    FIXED_ASSET_VEHICLE = "fixed_asset_vehicle"  # Vehicles (depreciated)
    FIXED_ASSET_FURNITURE = "fixed_asset_furniture"  # Furniture and fixtures (depreciated)
    FIXED_ASSET_LEASEHOLD = "fixed_asset_leasehold"  # Leasehold improvements (depreciated)
    FIXED_ASSET_COMPUTER = "fixed_asset_computer"  # Computer and IT equipment (depreciated)

    # Intangible Assets
    INTANGIBLE_PATENT = "intangible_patent"  # Patents (amortized)
    INTANGIBLE_LICENSE = "intangible_license"  # Licenses and copyrights (amortized)
    INTANGIBLE_GOODWILL = "intangible_goodwill"  # Goodwill (impairment tested)
    INTANGIBLE_SOFTWARE = "intangible_software"  # Software licenses (amortized)

    # Expense Items
    EXPENSE_GENERAL = "expense_general"  # General operating expenses
    EXPENSE_UTILITIES = "expense_utilities"  # Electricity, water, gas
    EXPENSE_SUPPLIES = "expense_supplies"  # Office supplies, consumables
    EXPENSE_MAINTENANCE = "expense_maintenance"  # Repairs and maintenance
    EXPENSE_PROFESSIONAL = "expense_professional"  # Consulting, legal fees
    EXPENSE_TRAVEL = "expense_travel"  # Travel and entertainment
    EXPENSE_INSURANCE = "expense_insurance"  # Insurance premiums
    EXPENSE_RENT = "expense_rent"  # Rent and lease expenses
    EXPENSE_ADVERTISING = "expense_advertising"  # Marketing and advertising
    EXPENSE_DELIVERY = "expense_delivery"  # Shipping and freight

    # Current Assets
    CURRENT_ASSET_PREPAID = "current_asset_prepaid"  # Prepaid expenses
    CURRENT_ASSET_DEPOSIT = "current_asset_deposit"  # Deposits and guarantees
    CURRENT_ASSET_RECEIVABLE = "current_asset_receivable"  # Advances to suppliers

    # Other
    OTHER = "other"  # Miscellaneous items


class GLAccountMapping:
    """
    Mapping of ItemType to GL Account codes for standard chart of accounts.
    This is used to automatically route purchases to the correct expense/asset accounts.
    """

    # Standard GL Account Codes (based on typical chart of accounts)
    MAPPING = {
        # INVENTORY ACCOUNTS (1xxx series - Assets)
        ItemTypeEnum.INVENTORY: {
            'debit_account': '1110',  # Merchandise Inventory / Inventory
            'description': 'Merchandise Inventory'
        },
        ItemTypeEnum.FINISHED_GOODS: {
            'debit_account': '1115',  # Finished Goods Inventory
            'description': 'Finished Goods'
        },
        ItemTypeEnum.COMPONENTS: {
            'debit_account': '1120',  # Component Inventory
            'description': 'Component Parts Inventory'
        },
        ItemTypeEnum.RAW_MATERIALS: {
            'debit_account': '1105',  # Raw Materials Inventory
            'description': 'Raw Materials Inventory'
        },

        # FIXED ASSETS (12xx series - Property, Plant & Equipment)
        ItemTypeEnum.FIXED_ASSET_BUILDING: {
            'debit_account': '1201',  # Building and Structures
            'accumulated_depreciation': '1201-A',  # Accumulated Depreciation - Building
            'depreciation_expense': '5201',  # Depreciation - Building
            'useful_life_years': 20,
            'description': 'Building and Structures'
        },
        ItemTypeEnum.FIXED_ASSET_MACHINERY: {
            'debit_account': '1205',  # Machinery and Equipment
            'accumulated_depreciation': '1205-A',  # Accumulated Depreciation - Machinery
            'depreciation_expense': '5205',  # Depreciation - Machinery
            'useful_life_years': 7,
            'description': 'Machinery and Equipment'
        },
        ItemTypeEnum.FIXED_ASSET_VEHICLE: {
            'debit_account': '1210',  # Motor Vehicles
            'accumulated_depreciation': '1210-A',  # Accumulated Depreciation - Vehicles
            'depreciation_expense': '5210',  # Depreciation - Vehicles
            'useful_life_years': 5,
            'description': 'Motor Vehicles'
        },
        ItemTypeEnum.FIXED_ASSET_FURNITURE: {
            'debit_account': '1215',  # Furniture and Fixtures
            'accumulated_depreciation': '1215-A',  # Accumulated Depreciation - Furniture
            'depreciation_expense': '5215',  # Depreciation - Furniture
            'useful_life_years': 7,
            'description': 'Furniture and Fixtures'
        },
        ItemTypeEnum.FIXED_ASSET_LEASEHOLD: {
            'debit_account': '1220',  # Leasehold Improvements
            'accumulated_depreciation': '1220-A',  # Accumulated Depreciation - Leasehold
            'depreciation_expense': '5220',  # Depreciation - Leasehold
            'useful_life_years': 10,
            'description': 'Leasehold Improvements'
        },
        ItemTypeEnum.FIXED_ASSET_COMPUTER: {
            'debit_account': '1230',  # Computer and IT Equipment
            'accumulated_depreciation': '1230-A',  # Accumulated Depreciation - Computer
            'depreciation_expense': '5230',  # Depreciation - Computer and IT Equipment
            'useful_life_years': 3,
            'description': 'Computer and IT Equipment'
        },

        # INTANGIBLE ASSETS (13xx series)
        ItemTypeEnum.INTANGIBLE_PATENT: {
            'debit_account': '1301',  # Patents
            'accumulated_amortization': '1301-A',  # Accumulated Amortization - Patents
            'amortization_expense': '5301',  # Amortization - Patents
            'useful_life_years': 17,  # Patent life in many countries
            'description': 'Patents and Patent Rights'
        },
        ItemTypeEnum.INTANGIBLE_LICENSE: {
            'debit_account': '1305',  # Licenses and Copyrights
            'accumulated_amortization': '1305-A',  # Accumulated Amortization - Licenses
            'amortization_expense': '5305',  # Amortization - Licenses
            'useful_life_years': 10,
            'description': 'Licenses and Copyrights'
        },
        ItemTypeEnum.INTANGIBLE_GOODWILL: {
            'debit_account': '1310',  # Goodwill
            'description': 'Goodwill',
            'impairment_account': '5350'  # Impairment Loss on Goodwill
        },
        ItemTypeEnum.INTANGIBLE_SOFTWARE: {
            'debit_account': '1315',  # Software and IT Intangibles
            'accumulated_amortization': '1315-A',  # Accumulated Amortization - Software
            'amortization_expense': '5315',  # Amortization - Software
            'useful_life_years': 5,
            'description': 'Software and IT Intangibles'
        },

        # EXPENSE ACCOUNTS (50xx-70xx series)
        ItemTypeEnum.EXPENSE_GENERAL: {
            'debit_account': '5010',  # General Operating Expenses
            'description': 'General Operating Expenses'
        },
        ItemTypeEnum.EXPENSE_UTILITIES: {
            'debit_account': '5020',  # Utilities Expense
            'description': 'Utilities (Electricity, Water, Gas)'
        },
        ItemTypeEnum.EXPENSE_SUPPLIES: {
            'debit_account': '5030',  # Supplies Expense
            'description': 'Office Supplies and Consumables'
        },
        ItemTypeEnum.EXPENSE_MAINTENANCE: {
            'debit_account': '5040',  # Repairs and Maintenance
            'description': 'Repairs and Maintenance'
        },
        ItemTypeEnum.EXPENSE_PROFESSIONAL: {
            'debit_account': '5050',  # Professional Fees
            'description': 'Professional Services (Consulting, Legal, Accounting)'
        },
        ItemTypeEnum.EXPENSE_TRAVEL: {
            'debit_account': '5060',  # Travel and Entertainment
            'description': 'Travel, Accommodation, Entertainment'
        },
        ItemTypeEnum.EXPENSE_INSURANCE: {
            'debit_account': '5070',  # Insurance Expense
            'description': 'Insurance Premiums'
        },
        ItemTypeEnum.EXPENSE_RENT: {
            'debit_account': '5080',  # Rent and Lease Expense
            'description': 'Rent and Lease Payments'
        },
        ItemTypeEnum.EXPENSE_ADVERTISING: {
            'debit_account': '5090',  # Advertising and Marketing
            'description': 'Advertising and Marketing Expenses'
        },
        ItemTypeEnum.EXPENSE_DELIVERY: {
            'debit_account': '5100',  # Shipping and Freight
            'description': 'Shipping, Freight and Delivery'
        },

        # CURRENT ASSETS (11xx series - but different from inventory)
        ItemTypeEnum.CURRENT_ASSET_PREPAID: {
            'debit_account': '1150',  # Prepaid Expenses
            'description': 'Prepaid Expenses and Deferred Charges'
        },
        ItemTypeEnum.CURRENT_ASSET_DEPOSIT: {
            'debit_account': '1155',  # Deposits and Guarantees
            'description': 'Deposits and Security Guarantees'
        },
        ItemTypeEnum.CURRENT_ASSET_RECEIVABLE: {
            'debit_account': '1160',  # Advances to Suppliers
            'description': 'Advances to Suppliers and Advances to Staff'
        },

        # OTHER
        ItemTypeEnum.OTHER: {
            'debit_account': '5900',  # Miscellaneous Expense
            'description': 'Miscellaneous and Sundry Expenses'
        }
    }

    @classmethod
    def get_gl_account(cls, item_type: ItemTypeEnum) -> dict:
        """
        Get GL account mapping for a specific item type.

        Args:
            item_type: ItemTypeEnum value

        Returns:
            Dictionary with GL account details or raises KeyError if not found
        """
        return cls.MAPPING.get(item_type, cls.MAPPING[ItemTypeEnum.OTHER])

    @classmethod
    def get_debit_account(cls, item_type: ItemTypeEnum) -> str:
        """Get the main debit account code for an item type"""
        return cls.get_gl_account(item_type).get('debit_account')

    @classmethod
    def is_depreciable(cls, item_type: ItemTypeEnum) -> bool:
        """Check if item type is subject to depreciation"""
        mapping = cls.get_gl_account(item_type)
        return 'accumulated_depreciation' in mapping

    @classmethod
    def is_amortizable(cls, item_type: ItemTypeEnum) -> bool:
        """Check if item type is subject to amortization"""
        mapping = cls.get_gl_account(item_type)
        return 'accumulated_amortization' in mapping

    @classmethod
    def is_asset(cls, item_type: ItemTypeEnum) -> bool:
        """Check if item type is an asset (vs. expense)"""
        asset_types = [
            ItemTypeEnum.INVENTORY,
            ItemTypeEnum.FINISHED_GOODS,
            ItemTypeEnum.COMPONENTS,
            ItemTypeEnum.RAW_MATERIALS,
            ItemTypeEnum.FIXED_ASSET_BUILDING,
            ItemTypeEnum.FIXED_ASSET_MACHINERY,
            ItemTypeEnum.FIXED_ASSET_VEHICLE,
            ItemTypeEnum.FIXED_ASSET_FURNITURE,
            ItemTypeEnum.FIXED_ASSET_LEASEHOLD,
            ItemTypeEnum.FIXED_ASSET_COMPUTER,
            ItemTypeEnum.INTANGIBLE_PATENT,
            ItemTypeEnum.INTANGIBLE_LICENSE,
            ItemTypeEnum.INTANGIBLE_GOODWILL,
            ItemTypeEnum.INTANGIBLE_SOFTWARE,
            ItemTypeEnum.CURRENT_ASSET_PREPAID,
            ItemTypeEnum.CURRENT_ASSET_DEPOSIT,
            ItemTypeEnum.CURRENT_ASSET_RECEIVABLE,
        ]
        return item_type in asset_types


class ItemTypeDefinition(BaseModel):
    """
    Database model for configurable item type definitions.
    Allows organizations to customize GL account mappings per item type.
    """
    __tablename__ = "item_type_definitions"

    id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4()))

    # Item type identifier
    item_type = Column(String, unique=True, nullable=False, index=True)
    name = Column(String, nullable=False)
    description = Column(Text)

    # GL Account mappings
    debit_account_id = Column(String, ForeignKey("accounting_codes.id"), nullable=False)
    credit_account_id = Column(String, ForeignKey("accounting_codes.id"), nullable=True)

    # For depreciable/amortizable assets
    accumulated_depr_account_id = Column(String, ForeignKey("accounting_codes.id"), nullable=True)
    depreciation_expense_account_id = Column(String, ForeignKey("accounting_codes.id"), nullable=True)
    useful_life_years = Column(Numeric(5, 1), nullable=True)

    # For intangible assets
    accumulated_amort_account_id = Column(String, ForeignKey("accounting_codes.id"), nullable=True)
    amortization_expense_account_id = Column(String, ForeignKey("accounting_codes.id"), nullable=True)

    # Configuration flags
    is_asset = Column(Boolean, default=True)
    is_taxable = Column(Boolean, default=True)
    requires_serial_number = Column(Boolean, default=False)
    is_active = Column(Boolean, default=True)

    # Relationships
    debit_account = relationship("AccountingCode", foreign_keys=[debit_account_id])
    credit_account = relationship("AccountingCode", foreign_keys=[credit_account_id])
    accumulated_depr_account = relationship("AccountingCode", foreign_keys=[accumulated_depr_account_id])
    depreciation_expense_account = relationship("AccountingCode", foreign_keys=[depreciation_expense_account_id])
    accumulated_amort_account = relationship("AccountingCode", foreign_keys=[accumulated_amort_account_id])
    amortization_expense_account = relationship("AccountingCode", foreign_keys=[amortization_expense_account_id])

    def __repr__(self):
        return f"<ItemTypeDefinition {self.item_type}: {self.name}>"
