from manim import *
import numpy as np

class EpicAdReportCardV2(ThreeDScene):
    def construct(self):
        self.camera.background_color = "#0a0a1a"
        self.set_camera_orientation(phi=0 * DEGREES, theta=-90 * DEGREES, zoom=0.8)
        
        # ============ ACT 1: EPIC INTRO WITH 3D SHAPES ============
        
        # Create a grid of cubes that form the backdrop
        cubes = VGroup()
        for x in range(-4, 5):
            for y in range(-3, 4):
                cube = Cube(side_length=0.3, fill_color=BLUE_E, fill_opacity=0.15, stroke_width=0.5, stroke_color=BLUE_D)
                cube.shift(x * 0.6 * RIGHT + y * 0.6 * UP)
                cubes.add(cube)
        
        # Scatter cubes in z-space initially
        for i, cube in enumerate(cubes):
            cube.shift(OUT * np.random.uniform(-8, -3))
            cube.set_opacity(0)
        
        # Fly cubes into grid formation
        self.play(
            *[cube.animate.shift(IN * np.random.uniform(3, 8)).set_opacity(0.15) for cube in cubes],
            run_time=2,
            rate_func=rush_from
        )
        
        # ============ ACT 2: TITLE SLAM ============
        
        # Title - positioned flat facing camera
        title_line1 = Text("ADVERTISING", font_size=84, color=WHITE, weight=BOLD)
        title_line2 = Text("REPORT CARD", font_size=84, color=GOLD, weight=BOLD)
        title_line2.next_to(title_line1, DOWN, buff=0.2)
        title_group = VGroup(title_line1, title_line2)
        title_group.shift(UP * 0.5)
        
        # Scale up from nothing with flash
        title_group.scale(0.01)
        
        flash = Circle(radius=0.1, color=GOLD, fill_opacity=1, stroke_width=0)
        
        self.play(
            title_group.animate.scale(100),
            flash.animate.scale(80).set_opacity(0),
            run_time=1,
            rate_func=rush_from
        )
        self.remove(flash)
        
        # Pulse effect
        self.play(
            title_group.animate.scale(1.15),
            rate_func=there_and_back,
            run_time=0.4
        )
        self.play(
            title_group.animate.scale(1.08),
            rate_func=there_and_back,
            run_time=0.3
        )
        
        self.wait(0.5)
        
        # ============ ACT 3: CAMERA TILT TO REVEAL 3D DEPTH ============
        
        # Now tilt the camera to show depth
        self.move_camera(phi=55 * DEGREES, theta=-70 * DEGREES, run_time=2, added_anims=[
            title_group.animate.shift(UP * 1.5),
            cubes.animate.set_opacity(0.08)
        ])
        
        # ============ ACT 4: 3D RATING BARS ============
        
        labels_data = [
            ("IMPACT", RED_C, 0.95),
            ("CLARITY", BLUE_C, 0.88),
            ("ROI", GREEN_C, 0.92),
            ("CREATIVITY", YELLOW_C, 0.97),
            ("STRATEGY", PURPLE_C, 0.90),
        ]
        
        all_bars = VGroup()
        
        for i, (label, color, rating) in enumerate(labels_data):
            # 3D bar using a rectangular prism
            bar_width = 5 * rating
            bar = Prism(dimensions=[bar_width, 0.35, 0.25], fill_color=color, fill_opacity=0.85, stroke_width=1, stroke_color=WHITE)
            bar.shift(LEFT * (5 - bar_width) / 2)  # align left
            
            # Background bar
            bg_bar = Prism(dimensions=[5, 0.35, 0.25], fill_color=GRAY, fill_opacity=0.2, stroke_width=0.5, stroke_color=GRAY)
            
            # Label
            label_text = Text(label, font_size=22, color=WHITE, weight=BOLD)
            label_text.next_to(bg_bar, LEFT, buff=0.4)
            
            # Percentage
            pct = Text(f"{int(rating*100)}%", font_size=28, color=color, weight=BOLD)
            pct.next_to(bg_bar, RIGHT, buff=0.4)
            
            row = VGroup(label_text, bg_bar, bar, pct)
            row.shift(DOWN * i * 0.65)
            all_bars.add(row)
        
        all_bars.shift(DOWN * 0.8)
        
        # Animate each bar sliding in from the left
        for i, row in enumerate(all_bars):
            label_text, bg_bar, bar, pct = row
            
            # Start positions
            bar.save_state()
            bar_target = bar.copy()
            bar.stretch(0.01, 0)  # squish to nothing
            bar.align_to(bg_bar, LEFT)
            
            self.play(
                FadeIn(label_text, shift=RIGHT * 0.5),
                FadeIn(bg_bar),
                run_time=0.2
            )
            self.play(
                bar.animate.restore(),
                FadeIn(pct, shift=LEFT * 0.3),
                run_time=0.4,
                rate_func=rush_from
            )
        
        self.wait(0.5)
        
        # ============ ACT 5: EPIC CAMERA ORBIT ============
        
        self.begin_ambient_camera_rotation(rate=0.15)
        self.wait(4)
        self.stop_ambient_camera_rotation()
        
        # ============ ACT 6: GRAND FINALE ============
        
        # Return camera to front
        self.move_camera(phi=0 * DEGREES, theta=-90 * DEGREES, run_time=1.5)
        
        # Clear bars with style
        self.play(
            *[row.animate.shift(RIGHT * 15) for row in all_bars],
            title_group.animate.shift(UP * 8),
            cubes.animate.set_opacity(0),
            run_time=1,
            rate_func=rush_into
        )
        self.remove(all_bars, title_group, cubes)
        
        # Final epic text
        final1 = Text("EXCELLENCE", font_size=120, color=GOLD, weight=BOLD)
        final2 = Text("DELIVERED", font_size=120, color=WHITE, weight=BOLD)
        final2.next_to(final1, DOWN, buff=0.15)
        finale = VGroup(final1, final2)
        
        # Star burst effect
        lines = VGroup()
        for angle in range(0, 360, 15):
            line = Line(ORIGIN, RIGHT * 8, color=GOLD, stroke_width=2, stroke_opacity=0.6)
            line.rotate(angle * DEGREES)
            lines.add(line)
        
        lines.scale(0.01)
        finale.scale(0.01)
        
        self.play(
            finale.animate.scale(100),
            lines.animate.scale(100).set_opacity(0),
            run_time=1.2,
            rate_func=rush_from
        )
        self.remove(lines)
        
        # Final pulse
        self.play(
            finale.animate.scale(1.1),
            rate_func=there_and_back,
            run_time=0.5
        )
        
        # Add a subtle glow ring
        ring = Annulus(inner_radius=2.5, outer_radius=3, color=GOLD, fill_opacity=0.3, stroke_width=0)
        ring.shift(DOWN * 0.3)
        
        self.play(
            FadeIn(ring),
            ring.animate.scale(1.5).set_opacity(0),
            run_time=1.5
        )
        self.remove(ring)
        
        self.wait(1.5)
