#!/usr/bin/env python3
"""
Unit tests for the alignment handler system.
Tests the various alignment handlers (Left, Center, Right, Justify) and their integration with Line objects.
"""

import unittest

from pyWebLayout.concrete.text import (
    Line, Text, LeftAlignmentHandler, CenterRightAlignmentHandler, JustifyAlignmentHandler
)
from pyWebLayout.style import Alignment
from pyWebLayout.style import Font
from pyWebLayout.abstract import Word
from PIL import Image, ImageDraw


class TestAlignmentHandlers(unittest.TestCase):
    """Test cases for the alignment handler system"""

    def setUp(self):
        """Set up test fixtures"""
        self.font = Font()
        self.test_words = [Word(text, self.font)
                           for text in ["This", "is", "a", "test", "sentence"]]
        self.line_width = 300
        self.line_height = 30
        self.spacing = (5, 20)  # min_spacing, max_spacing
        self.origin = (0, 0)
        self.size = (self.line_width, self.line_height)

        # Create a real PIL image (canvas) for testing
        self.canvas = Image.new('RGB', (800, 600), color='white')

        # Create a real ImageDraw object
        self.draw = ImageDraw.Draw(self.canvas)

        # Create a real Font object
        self.style = Font()

    def test_left_alignment_handler_assignment(self):
        """Test that Line correctly assigns LeftAlignmentHandler for LEFT alignment"""
        left_line = Line(
            self.spacing,
            self.origin,
            self.size,
            self.draw,
            font=self.style,
            halign=Alignment.LEFT)

        self.assertIsInstance(left_line._alignment_handler, LeftAlignmentHandler)

    def test_center_alignment_handler_assignment(self):
        """Test that Line correctly assigns CenterRightAlignmentHandler for CENTER alignment"""
        center_line = Line(
            self.spacing,
            self.origin,
            self.size,
            self.draw,
            font=self.style,
            halign=Alignment.CENTER)

        self.assertIsInstance(
            center_line._alignment_handler,
            CenterRightAlignmentHandler)
        # Check that it's configured for CENTER alignment
        self.assertEqual(center_line._alignment_handler._alignment, Alignment.CENTER)

    def test_right_alignment_handler_assignment(self):
        """Test that Line correctly assigns CenterRightAlignmentHandler for RIGHT alignment"""
        right_line = Line(
            self.spacing,
            self.origin,
            self.size,
            self.draw,
            font=self.style,
            halign=Alignment.RIGHT)

        self.assertIsInstance(
            right_line._alignment_handler,
            CenterRightAlignmentHandler)
        # Check that it's configured for RIGHT alignment
        self.assertEqual(right_line._alignment_handler._alignment, Alignment.RIGHT)

    def test_justify_alignment_handler_assignment(self):
        """Test that Line correctly assigns JustifyAlignmentHandler for JUSTIFY alignment"""
        justify_line = Line(
            self.spacing,
            self.origin,
            self.size,
            self.draw,
            font=self.style,
            halign=Alignment.JUSTIFY)

        self.assertIsInstance(justify_line._alignment_handler, JustifyAlignmentHandler)

    def test_left_alignment_word_addition(self):
        """Test adding words to a left-aligned line"""
        left_line = Line(
            self.spacing,
            self.origin,
            self.size,
            self.draw,
            halign=Alignment.LEFT)

        # Add words until line is full or we run out
        words_added = 0
        for word in self.test_words:
            result, part = left_line.add_word(word)
            if not result:
                # Word didn't fit
                break
            else:
                words_added += 1

        # Should have added at least some words
        self.assertGreater(words_added, 0)
        self.assertEqual(len(left_line.text_objects), words_added)

    def test_center_alignment_word_addition(self):
        """Test adding words to a center-aligned line"""
        center_line = Line(
            self.spacing,
            self.origin,
            self.size,
            self.draw,
            font=self.style,
            halign=Alignment.CENTER)

        # Add words until line is full or we run out
        words_added = 0
        for word in self.test_words:
            result, part = center_line.add_word(word)
            if not result:
                # Word didn't fit
                break
            else:
                words_added += 1

        # Should have added at least some words
        self.assertGreater(words_added, 0)
        self.assertEqual(len(center_line.text_objects), words_added)

    def test_right_alignment_word_addition(self):
        """Test adding words to a right-aligned line"""
        right_line = Line(
            self.spacing,
            self.origin,
            self.size,
            self.draw,
            font=self.style,
            halign=Alignment.RIGHT)

        # Add words until line is full or we run out
        words_added = 0
        for word in self.test_words:
            result, part = right_line.add_word(word)
            if not result:
                # Word didn't fit
                break
            else:
                words_added += 1

        # Should have added at least some words
        self.assertGreater(words_added, 0)
        self.assertEqual(len(right_line.text_objects), words_added)

    def test_justify_alignment_word_addition(self):
        """Test adding words to a justified line"""
        justify_line = Line(
            self.spacing,
            self.origin,
            self.size,
            self.draw,
            font=self.style,
            halign=Alignment.JUSTIFY)

        # Add words until line is full or we run out
        words_added = 0
        for word in self.test_words:
            result, part = justify_line.add_word(word)
            if not result:
                # Word didn't fit
                break
            else:
                words_added += 1

        # Should have added at least some words
        self.assertGreater(words_added, 0)
        self.assertEqual(len(justify_line.text_objects), words_added)

    def test_handler_spacing_and_position_calculations(self):
        """Test spacing and position calculations for different alignment handlers"""
        # Create sample text objects
        text_objects = [Text(word, self.style, self.draw)
                        for word in ["Hello", "World"]]

        # Test each handler type
        handlers = [
            ("Left", LeftAlignmentHandler()),
            ("Center", CenterRightAlignmentHandler(Alignment.CENTER)),
            ("Right", CenterRightAlignmentHandler(Alignment.RIGHT)),
            ("Justify", JustifyAlignmentHandler())
        ]

        for name, handler in handlers:
            with self.subTest(handler=name):
                spacing_calc, position, overflow = handler.calculate_spacing_and_position(
                    text_objects, self.line_width, self.spacing[0], self.spacing[1])

                # Check that spacing is a valid number
                self.assertIsInstance(spacing_calc, (int, float))
                self.assertGreaterEqual(spacing_calc, 0)

                # Check that position is a valid number
                self.assertIsInstance(position, (int, float))
                self.assertGreaterEqual(position, 0)

                # Check that overflow is a boolean
                self.assertIsInstance(overflow, bool)

                # Position should be within line width (unless overflow)
                if not overflow:
                    self.assertLessEqual(position, self.line_width)

    def test_left_handler_spacing_calculation(self):
        """Test specific spacing calculation for left alignment"""
        handler = LeftAlignmentHandler()
        text_objects = [Text(word, self.style, self.draw)
                        for word in ["Hello", "World"]]

        spacing_calc, position, overflow = handler.calculate_spacing_and_position(
            text_objects, self.line_width, self.spacing[0], self.spacing[1])

        # Left alignment should have position at 0
        self.assertEqual(position, 0)

        # Should not overflow with reasonable text
        self.assertFalse(overflow)

    def test_center_handler_spacing_calculation(self):
        """Test specific spacing calculation for center alignment"""
        handler = CenterRightAlignmentHandler(Alignment.CENTER)
        text_objects = [Text(word, self.style, self.draw)
                        for word in ["Hello", "World"]]

        spacing_calc, position, overflow = handler.calculate_spacing_and_position(
            text_objects, self.line_width, self.spacing[0], self.spacing[1])

        # Center alignment should have position > 0 (centered) if no overflow
        if not overflow:
            self.assertGreaterEqual(position, 0)

    def test_right_handler_spacing_calculation(self):
        """Test specific spacing calculation for right alignment"""
        handler = CenterRightAlignmentHandler(Alignment.RIGHT)
        text_objects = [Text(word, self.style, self.draw)
                        for word in ["Hello", "World"]]

        spacing_calc, position, overflow = handler.calculate_spacing_and_position(
            text_objects, self.line_width, self.spacing[0], self.spacing[1])

        # Right alignment should have position >= 0
        self.assertGreaterEqual(position, 0)

    def test_justify_handler_spacing_calculation(self):
        """Test specific spacing calculation for justify alignment"""
        handler = JustifyAlignmentHandler()
        text_objects = [Text(word, self.style, self.draw)
                        for word in ["Hello", "World"]]

        spacing_calc, position, overflow = handler.calculate_spacing_and_position(
            text_objects, self.line_width, self.spacing[0], self.spacing[1])

        # Justify alignment should have position at 0
        self.assertEqual(position, 0)

        # Check spacing is reasonable
        self.assertGreaterEqual(spacing_calc, 0)

    def test_empty_line_alignment_handlers(self):
        """Test alignment handlers with empty lines"""
        alignments = [
            Alignment.LEFT,
            Alignment.CENTER,
            Alignment.RIGHT,
            Alignment.JUSTIFY]

        for alignment in alignments:
            with self.subTest(alignment=alignment):
                line = Line(
                    self.spacing,
                    self.origin,
                    self.size,
                    self.draw,
                    font=self.style,
                    halign=alignment)

                # Empty line should still have a handler
                self.assertIsNotNone(line._alignment_handler)

                # Should be able to render empty line
                line.render()

    def test_single_word_line_alignment(self):
        """Test alignment handlers with single word lines"""
        alignments = [
            Alignment.LEFT,
            Alignment.CENTER,
            Alignment.RIGHT,
            Alignment.JUSTIFY]

        for alignment in alignments:
            with self.subTest(alignment=alignment):
                line = Line(
                    self.spacing,
                    self.origin,
                    self.size,
                    self.draw,
                    font=self.style,
                    halign=alignment)

                # Create a test word
                test_word = Word("test", self.style)

                # Add a single word
                result, part = line.add_word(test_word)
                self.assertTrue(result)  # Should fit
                self.assertIsNone(part)  # No overflow part

                # Should be able to render single word line
                line.render()
                self.assertEqual(len(line.text_objects), 1)


if __name__ == '__main__':
    unittest.main()
