#!/usr/bin/env python
"""
測試 RingBuffer 類別的功能
"""

import unittest
import threading
import time
from concurrent.futures import ThreadPoolExecutor

from ..fc_network_adapter.utils import RingBuffer

class TestRingBuffer(unittest.TestCase):
    """測試 RingBuffer 基本功能"""
    
    def setUp(self):
        """每個測試前的準備"""
        self.buffer = RingBuffer(capacity=8)
    
    def test_initialization(self):
        """測試初始化"""
        self.assertEqual(self.buffer.capacity, 8)
        self.assertTrue(self.buffer.is_empty())
        self.assertFalse(self.buffer.is_full())
        self.assertEqual(self.buffer.size(), 0)
    
    def test_put_get_basic(self):
        """測試基本的放入和取出"""
        # 測試放入
        self.assertTrue(self.buffer.put("item1"))
        self.assertTrue(self.buffer.put("item2"))
        self.assertEqual(self.buffer.size(), 2)
        self.assertFalse(self.buffer.is_empty())
        
        # 測試取出
        item1 = self.buffer.get()
        self.assertEqual(item1, "item1")
        self.assertEqual(self.buffer.size(), 1)
        
        item2 = self.buffer.get()
        self.assertEqual(item2, "item2")
        self.assertTrue(self.buffer.is_empty())
        
        # 空緩衝區取出應返回 None
        self.assertIsNone(self.buffer.get())
    
    def test_capacity_overflow(self):
        """測試緩衝區容量限制"""
        # 填滿緩衝區 (容量-1，因為需要預留一個位置)
        for i in range(7):  # 8-1=7
            self.assertTrue(self.buffer.put(f"item{i}"))
        
        self.assertTrue(self.buffer.is_full())
        
        # 嘗試再放入一個應該失敗
        self.assertFalse(self.buffer.put("overflow"))
        self.assertEqual(self.buffer.overflow_count.value, 1)
    
    def test_get_all(self):
        """測試取出所有項目"""
        items = ["a", "b", "c", "d"]
        for item in items:
            self.buffer.put(item)
        
        all_items = self.buffer.get_all()
        self.assertEqual(all_items, items)
        self.assertTrue(self.buffer.is_empty())
    
    def test_clear(self):
        """測試清空緩衝區"""
        for i in range(5):
            self.buffer.put(f"item{i}")
        
        self.buffer.clear()
        self.assertTrue(self.buffer.is_empty())
        self.assertEqual(self.buffer.size(), 0)


class TestRingBufferThreadSafety(unittest.TestCase):
    """測試 RingBuffer 的線程安全性"""
    
    def setUp(self):
        """每個測試前的準備"""
        self.buffer = RingBuffer(capacity=256)
        self.results = []
        self.write_count = 1000
        self.read_count = 1000
    
    def test_concurrent_producers_consumers(self):
        """測試多生產者多消費者場景"""
        self.results = []
        stats = self.buffer.get_stats()
        self.assertEqual(stats['total_writes'], 0)
        
        def producer(producer_id, count):
            """生產者函數"""
            for i in range(count):
                item = f"producer_{producer_id}_item_{i}"
                while not self.buffer.put(item):
                    time.sleep(0.001)  # 緩衝區滿時稍微等待
        
        def consumer(consumer_id, count):
            """消費者函數"""
            items = []
            for _ in range(count):
                item = None
                while item is None:
                    item = self.buffer.get()
                    if item is None:
                        time.sleep(0.001)  # 緩衝區空時稍微等待
                items.append(item)
            self.results.extend(items)
        
        # 創建多個生產者和消費者
        with ThreadPoolExecutor(max_workers=8) as executor:
            # 2 個生產者，每個寫入 500 個項目
            producer_futures = [
                executor.submit(producer, 0, 500),
                executor.submit(producer, 1, 500)
            ]
            
            # 2 個消費者，每個讀取 500 個項目
            consumer_futures = [
                executor.submit(consumer, 0, 500),
                executor.submit(consumer, 1, 500)
            ]
            
            # 等待所有任務完成
            for future in producer_futures + consumer_futures:
                future.result()
        
        # 驗證結果
        self.assertEqual(len(self.results), 1000)
        self.assertTrue(self.buffer.is_empty())
        
        # 檢查統計數據
        stats = self.buffer.get_stats()
        self.assertEqual(stats['total_writes'], 1000)
        self.assertGreater(stats['total_reads'], 1000)  # 包含失敗的讀取嘗試
        self.assertGreater(stats['write_threads'], 1)
        self.assertGreater(stats['read_threads'], 1)
    
    def test_high_throughput(self):
        """測試高吞吐量場景"""
        items_per_thread = 10000
        num_threads = 4
        
        def writer():
            for i in range(items_per_thread):
                while not self.buffer.put(i):
                    pass  # 忙等待
        
        def reader():
            items = []
            for _ in range(items_per_thread):
                item = None
                while item is None:
                    item = self.buffer.get()
                items.append(item)
            self.results.extend(items)
        
        start_time = time.time()
        
        with ThreadPoolExecutor(max_workers=num_threads * 2) as executor:
            # 啟動寫入線程
            writer_futures = [executor.submit(writer) for _ in range(num_threads)]
            
            # 啟動讀取線程
            reader_futures = [executor.submit(reader) for _ in range(num_threads)]
            
            # 等待完成
            for future in writer_futures + reader_futures:
                future.result()
        
        end_time = time.time()
        
        # 驗證結果
        total_items = items_per_thread * num_threads
        self.assertEqual(len(self.results), total_items)
        
        # 性能統計
        duration = end_time - start_time
        throughput = total_items / duration
        
        print(f"\nHigh Throughput Test Results:")
        print(f"Total items: {total_items}")
        print(f"Duration: {duration:.2f}s")
        print(f"Throughput: {throughput:.0f} items/sec")
        
        # 顯示詳細統計
        self.buffer.print_stats()


class TestRingBufferStatistics(unittest.TestCase):
    """測試 RingBuffer 的統計功能"""
    
    def test_statistics_tracking(self):
        """測試統計數據追蹤"""
        buffer = RingBuffer(capacity=16)
        
        # 寫入一些數據
        for i in range(10):
            buffer.put(f"item{i}")
        
        # 讀取一些數據
        for _ in range(5):
            buffer.get()
        
        stats = buffer.get_stats()
        
        # 驗證基本統計
        self.assertEqual(stats['total_writes'], 10)
        self.assertEqual(stats['total_reads'], 5)
        self.assertEqual(stats['current_size'], 5)
        self.assertEqual(stats['write_threads'], 1)
        self.assertEqual(stats['read_threads'], 1)
        
    def test_reset_statistics(self):
        """測試重置統計數據"""
        buffer = RingBuffer(capacity=16)
        
        # 產生一些活動
        for i in range(5):
            buffer.put(f"item{i}")
        for _ in range(3):
            buffer.get()
        
        # 重置統計
        buffer.reset_stats()
        
        stats = buffer.get_stats()
        self.assertEqual(stats['total_writes'], 0)
        self.assertEqual(stats['total_reads'], 0)
        self.assertEqual(stats['concurrent_writes'], 0)
        self.assertEqual(stats['concurrent_reads'], 0)
        self.assertEqual(stats['overflow_count'], 0)


def benchmark_ringbuffer():
    """RingBuffer 性能基準測試"""
    print("\n=== RingBuffer Performance Benchmark ===")
    
    buffer = RingBuffer(capacity=1024)
    num_operations = 100000
    
    # 單線程性能測試
    start_time = time.time()
    for i in range(num_operations):
        buffer.put(i)
    for _ in range(num_operations):
        buffer.get()
    end_time = time.time()
    
    single_thread_time = end_time - start_time
    throughput = (num_operations * 2) / single_thread_time
    
    print(f"Single Thread: {throughput:.0f} ops/sec")
    
    # 多線程性能測試
    buffer = RingBuffer(capacity=1024)
    
    def producer():
        for i in range(num_operations // 2):
            while not buffer.put(i):
                pass
    
    def consumer():
        for _ in range(num_operations // 2):
            while buffer.get() is None:
                pass
    
    start_time = time.time()
    
    with ThreadPoolExecutor(max_workers=2) as executor:
        future1 = executor.submit(producer)
        future2 = executor.submit(consumer)
        future1.result()
        future2.result()
    
    end_time = time.time()
    
    multi_thread_time = end_time - start_time
    throughput = num_operations / multi_thread_time
    
    print(f"Multi Thread: {throughput:.0f} ops/sec")
    print(f"Speedup: {single_thread_time/multi_thread_time:.2f}x")
    
    buffer.print_stats()


if __name__ == "__main__":
    # 運行單元測試
    unittest.main(argv=[''], exit=False, verbosity=2)
    
    # 運行性能基準測試
    benchmark_ringbuffer()