// Render target — manages wgpu color and depth textures for off-screen rendering.
// No egui dependency. Consumers (editor, runtime) wrap this with their own presentation.

use crate::formats::{DEPTH_FORMAT, TEXTURE_FORMAT};

/// Off-screen render target with color and depth textures.
pub struct RenderTarget {
    pub width: u32,
    pub height: u32,
    pub texture: Option<wgpu::Texture>,
    pub texture_view: Option<wgpu::TextureView>,
    pub depth_texture: Option<wgpu::Texture>,
    pub depth_view: Option<wgpu::TextureView>,
    pub needs_resize: bool,
    pub frames_rendered: u64,
    pub fps: f32,
    last_frame_time: std::time::Instant,
}

impl Default for RenderTarget {
    fn default() -> Self {
        Self {
            width: 1,
            height: 1,
            texture: None,
            texture_view: None,
            depth_texture: None,
            depth_view: None,
            needs_resize: true,
            frames_rendered: 0,
            fps: 0.0,
            last_frame_time: std::time::Instant::now(),
        }
    }
}

impl RenderTarget {
    /// Update render target size. Returns true if resize occurred.
    pub fn update_size(&mut self, width: u32, height: u32) -> bool {
        let w = width.max(1);
        let h = height.max(1);
        if w != self.width || h != self.height {
            self.width = w;
            self.height = h;
            self.needs_resize = true;
            true
        } else {
            false
        }
    }

    /// Create/recreate render target textures.
    pub fn ensure_textures(&mut self, device: &wgpu::Device) {
        if !self.needs_resize {
            return;
        }
        self.needs_resize = false;

        // Color texture
        let texture = device.create_texture(&wgpu::TextureDescriptor {
            label: Some("render_target_color"),
            size: wgpu::Extent3d {
                width: self.width,
                height: self.height,
                depth_or_array_layers: 1,
            },
            mip_level_count: 1,
            sample_count: 1,
            dimension: wgpu::TextureDimension::D2,
            format: TEXTURE_FORMAT,
            usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
            view_formats: &[],
        });
        let view = texture.create_view(&wgpu::TextureViewDescriptor::default());

        // Depth texture
        let depth = device.create_texture(&wgpu::TextureDescriptor {
            label: Some("render_target_depth"),
            size: wgpu::Extent3d {
                width: self.width,
                height: self.height,
                depth_or_array_layers: 1,
            },
            mip_level_count: 1,
            sample_count: 1,
            dimension: wgpu::TextureDimension::D2,
            format: DEPTH_FORMAT,
            usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
            view_formats: &[],
        });
        let depth_view = depth.create_view(&wgpu::TextureViewDescriptor::default());

        self.texture = Some(texture);
        self.texture_view = Some(view);
        self.depth_texture = Some(depth);
        self.depth_view = Some(depth_view);
    }

    /// Track FPS.
    pub fn tick_fps(&mut self) {
        self.frames_rendered += 1;
        let now = std::time::Instant::now();
        let dt = now.duration_since(self.last_frame_time).as_secs_f32();
        if dt > 0.0 {
            self.fps = self.fps * 0.95 + (1.0 / dt) * 0.05;
        }
        self.last_frame_time = now;
    }

    pub fn aspect_ratio(&self) -> f32 {
        self.width as f32 / self.height.max(1) as f32
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn update_size_triggers_resize() {
        let mut rt = RenderTarget {
            needs_resize: false,
            ..RenderTarget::default()
        };
        assert!(rt.update_size(800, 600));
        assert!(rt.needs_resize);
    }

    #[test]
    fn same_size_no_resize() {
        let mut rt = RenderTarget {
            width: 800,
            height: 600,
            needs_resize: false,
            ..RenderTarget::default()
        };
        assert!(!rt.update_size(800, 600));
        assert!(!rt.needs_resize);
    }

    #[test]
    fn aspect_ratio_calculation() {
        let rt = RenderTarget {
            width: 1920,
            height: 1080,
            ..RenderTarget::default()
        };
        assert!((rt.aspect_ratio() - 16.0 / 9.0).abs() < 0.01);
    }
}
