from __future__ import annotations
import typing
import re
from urllib.parse import unquote
from ..base import BaseFileIterable, BaseCodec
from ..helpers.utils import rowincount


class CEFIterable(BaseFileIterable):
    """
    Common Event Format (CEF) reader/writer.
    CEF format: CEF:Version|Device Vendor|Device Product|Device Version|Signature ID|Name|Severity|Extension
    """
    datamode = 'text'
    
    def __init__(self, filename:str = None, stream:typing.IO = None, codec: BaseCodec = None, mode:str='r', encoding:str = 'utf8', options:dict={}):
        super(CEFIterable, self).__init__(filename, stream, codec=codec, binary=False, mode=mode, encoding=encoding, options=options)
        self.reset()
        pass

    def reset(self):
        """Reset iterable"""
        super(CEFIterable, self).reset()
        self.pos = 0

    @staticmethod
    def id() -> str:
        return 'cef'

    @staticmethod
    def is_flatonly() -> bool:
        return True

    @staticmethod
    def has_totals():
        """Has totals indicator"""
        return True        

    def totals(self):
        """Returns file totals"""
        if self.codec is not None:
            fobj = self.codec.fileobj()
        else:
            fobj = self.fobj
        return rowincount(self.filename, fobj)

    def _parse_cef(self, line: str) -> dict:
        """Parse CEF line into dictionary"""
        # CEF format: CEF:Version|Device Vendor|Device Product|Device Version|Signature ID|Name|Severity|Extension
        if not line.startswith('CEF:'):
            return {'raw_line': line}
        
        # Remove CEF: prefix
        line = line[4:]
        
        # Split by pipe, but handle escaped pipes in extension
        parts = []
        current = ''
        escape_next = False
        for char in line:
            if escape_next:
                current += char
                escape_next = False
            elif char == '\\':
                escape_next = True
            elif char == '|' and not escape_next:
                parts.append(current)
                current = ''
            else:
                current += char
        if current:
            parts.append(current)
        
        result = {}
        
        # Standard CEF fields
        if len(parts) > 0:
            result['cef_version'] = parts[0]
        if len(parts) > 1:
            result['device_vendor'] = parts[1]
        if len(parts) > 2:
            result['device_product'] = parts[2]
        if len(parts) > 3:
            result['device_version'] = parts[3]
        if len(parts) > 4:
            result['signature_id'] = parts[4]
        if len(parts) > 5:
            result['name'] = parts[5]
        if len(parts) > 6:
            result['severity'] = parts[6]
        
        # Parse extension (key=value pairs)
        if len(parts) > 7:
            extension = parts[7]
            # Parse extension fields (key=value pairs, space-separated)
            # Handle escaped spaces and equals signs
            ext_parts = re.split(r'(?<!\\)=', extension)
            i = 0
            while i < len(ext_parts) - 1:
                key = ext_parts[i].strip().replace('\\=', '=').replace('\\ ', ' ')
                value = ext_parts[i + 1]
                
                # Find the end of this value (next space that's not escaped, or end)
                # For simplicity, we'll split by spaces and reconstruct
                value_parts = []
                for j in range(i + 1, len(ext_parts)):
                    if j == i + 1:
                        value_parts.append(ext_parts[j])
                    else:
                        # Check if this starts a new key (has space before =)
                        if ' ' in ext_parts[j]:
                            space_idx = ext_parts[j].index(' ')
                            value_parts.append(ext_parts[j][:space_idx])
                            ext_parts[j] = ext_parts[j][space_idx+1:]
                            break
                        else:
                            value_parts.append(ext_parts[j])
                
                value = '='.join(value_parts).strip()
                # Unescape value
                value = value.replace('\\=', '=').replace('\\ ', ' ').replace('\\n', '\n').replace('\\r', '\r').replace('\\t', '\t')
                # URL decode
                try:
                    value = unquote(value)
                except:
                    pass
                
                result[key] = value
                i += len(value_parts)
        
        return result

    def read(self, skip_empty:bool = True) -> dict:
        """Read single CEF record"""
        while True:
            line = self.fobj.readline()
            if not line:
                raise StopIteration
            
            line = line.strip()
            if skip_empty and len(line) == 0:
                continue
            
            result = self._parse_cef(line)
            self.pos += 1
            return result

    def read_bulk(self, num:int = 10) -> list[dict]:
        """Read bulk CEF records"""
        chunk = []
        for n in range(0, num):
            try:
                chunk.append(self.read())
            except StopIteration:
                break
        return chunk

    def _escape_cef_value(self, value: str) -> str:
        """Escape CEF value"""
        return str(value).replace('\\', '\\\\').replace('|', '\\|').replace('=', '\\=').replace('\n', '\\n').replace('\r', '\\r').replace('\t', '\\t')

    def write(self, record:dict):
        """Write single CEF record"""
        # Build CEF line
        parts = []
        
        # Standard CEF fields
        parts.append(str(record.get('cef_version', '0')))
        parts.append(str(record.get('device_vendor', '-')))
        parts.append(str(record.get('device_product', '-')))
        parts.append(str(record.get('device_version', '-')))
        parts.append(str(record.get('signature_id', '-')))
        parts.append(str(record.get('name', '-')))
        parts.append(str(record.get('severity', '0')))
        
        # Build extension
        extension_parts = []
        for key, value in record.items():
            if key not in ['cef_version', 'device_vendor', 'device_product', 'device_version', 
                          'signature_id', 'name', 'severity']:
                escaped_key = self._escape_cef_value(key)
                escaped_value = self._escape_cef_value(value)
                extension_parts.append(f'{escaped_key}={escaped_value}')
        
        if extension_parts:
            parts.append(' '.join(extension_parts))
        
        line = 'CEF:' + '|'.join(parts) + '\n'
        self.fobj.write(line)

    def write_bulk(self, records: list[dict]):
        """Write bulk CEF records"""
        for record in records:
            self.write(record)
