"""
Fibre Channel Data decoder for Chapter 10 data types
Supports Fibre Channel Format 0 (0x79)
"""

import struct
from typing import Dict, Any, Optional
from .base import DataTypeDecoder, DecodedPayload


class FibreChannelDecoder(DataTypeDecoder):
    """Decoder for Fibre Channel Data type (0x79)"""
    
    def __init__(self):
        super().__init__()
        self.data_type_base = 0x79
        self.data_type_name = "Fibre Channel Data"
        self.supported_formats = [0x79]
    
    def can_decode(self, data_type: int) -> bool:
        return data_type == 0x79
    
    def get_data_type_name(self, data_type: int) -> str:
        return "Fibre Channel Data Format 0"
    
    def decode(self, payload: bytes, ch10_header: Dict[str, Any]) -> Optional[DecodedPayload]:
        """Decode Fibre Channel payload"""
        decoded_data = {}
        errors = []
        
        # Parse IPH
        iph = self._parse_intra_packet_header(payload)
        if iph:
            decoded_data.update(iph)
            data_start = iph['data_start']
        else:
            data_start = 0
            errors.append("Failed to parse intra-packet header")
        
        # Parse FC frame header
        if data_start + 24 <= len(payload):
            # FC frame header (simplified)
            fc_header = self._safe_unpack('<IIIIII', payload, data_start)
            if fc_header:
                decoded_data.update({
                    'fc_timestamp': fc_header[0],
                    'fc_frame_length': fc_header[1],
                    'fc_r_ctl': (fc_header[2] >> 24) & 0xFF,
                    'fc_d_id': fc_header[2] & 0xFFFFFF,
                    'fc_cs_ctl': (fc_header[3] >> 24) & 0xFF,
                    'fc_s_id': fc_header[3] & 0xFFFFFF,
                    'fc_type': (fc_header[4] >> 24) & 0xFF,
                    'fc_f_ctl': fc_header[4] & 0xFFFFFF,
                    'fc_seq_id': (fc_header[5] >> 24) & 0xFF,
                    'fc_df_ctl': (fc_header[5] >> 16) & 0xFF,
                    'fc_seq_cnt': fc_header[5] & 0xFFFF
                })
                
                # Decode R_CTL field
                r_ctl = decoded_data['fc_r_ctl']
                decoded_data['fc_r_ctl_description'] = self._decode_r_ctl(r_ctl)
                
                # Decode Type field
                fc_type = decoded_data['fc_type']
                decoded_data['fc_type_description'] = self._decode_fc_type(fc_type)
                
                # Extract payload
                fc_payload_start = data_start + 24
                frame_length = decoded_data['fc_frame_length']
                if fc_payload_start + frame_length <= len(payload):
                    fc_payload = payload[fc_payload_start:fc_payload_start + frame_length]
                    decoded_data['fc_payload_length'] = len(fc_payload)
                    decoded_data['fc_payload_preview'] = fc_payload[:64].hex()
                    
                    # Analyze payload based on type
                    if fc_type == 0x08:  # SCSI FCP
                        scsi_data = self._parse_scsi_fcp(fc_payload)
                        decoded_data.update(scsi_data)
                else:
                    errors.append("FC payload extends beyond packet")
            else:
                errors.append("Failed to parse FC header")
        
        return DecodedPayload(
            data_type=0x79,
            data_type_name="Fibre Channel Data Format 0",
            format_version=0,
            decoded_data=decoded_data,
            raw_payload=payload,
            errors=errors,
            metadata={'decoder': 'FibreChannelDecoder'}
        )
    
    def _decode_r_ctl(self, r_ctl: int) -> str:
        """Decode R_CTL field"""
        r_ctl_types = {
            0x00: "Device Data",
            0x01: "Extended Link Data", 
            0x02: "FC-4 Link Data",
            0x03: "Video Data",
            0x20: "Basic Link Data",
            0x21: "ACK_1",
            0x22: "ACK_0", 
            0x23: "P_RJT",
            0x24: "F_RJT",
            0x25: "P_BSY",
            0x26: "F_BSY"
        }
        return r_ctl_types.get(r_ctl, f"Unknown (0x{r_ctl:02x})")
    
    def _decode_fc_type(self, fc_type: int) -> str:
        """Decode FC Type field"""
        fc_types = {
            0x00: "Basic Link Service",
            0x01: "Extended Link Service",
            0x04: "IP over FC",
            0x05: "ATM over FC",
            0x08: "SCSI FCP",
            0x09: "SCSI GPP",
            0x0A: "IPI-3 Master",
            0x0B: "IPI-3 Slave",
            0x0C: "IPI-3 Peer"
        }
        return fc_types.get(fc_type, f"Unknown (0x{fc_type:02x})")
    
    def _parse_scsi_fcp(self, payload: bytes) -> Dict[str, Any]:
        """Parse SCSI FCP payload"""
        scsi_data = {}
        
        if len(payload) >= 32:
            # FCP_CMND structure
            lun = payload[0:8]
            task_codes = payload[8]
            task_mgmt = payload[9]
            add_cdb_len = payload[10]
            rddata = bool(payload[11] & 0x02)
            wrdata = bool(payload[11] & 0x01)
            
            scsi_data.update({
                'scsi_lun': lun.hex(),
                'scsi_task_codes': task_codes,
                'scsi_task_mgmt': task_mgmt,
                'scsi_rddata': rddata,
                'scsi_wrdata': wrdata
            })
            
            # CDB starts at offset 12
            if len(payload) >= 16:
                cdb = payload[12:16]
                scsi_data['scsi_cdb'] = cdb.hex()
                if cdb[0] in [0x12, 0x00, 0x28, 0x2A]:  # Common SCSI commands
                    scsi_data['scsi_command'] = self._decode_scsi_command(cdb[0])
        
        return scsi_data
    
    def _decode_scsi_command(self, opcode: int) -> str:
        """Decode SCSI command opcode"""
        commands = {
            0x00: "TEST UNIT READY",
            0x12: "INQUIRY",
            0x28: "READ(10)",
            0x2A: "WRITE(10)",
            0x35: "SYNCHRONIZE CACHE",
            0x3C: "READ BUFFER"
        }
        return commands.get(opcode, f"Unknown (0x{opcode:02x})")
