"""
FormFromLight · Blender add-on
==============================

Hits the FormFromLight REST API to list your generated 3D models and import
their GLBs directly into the scene · no manual download dance.

Install:
  1. Edit > Preferences > Add-ons > Install...
  2. Pick this .py file
  3. Tick "FormFromLight" in the list
  4. Paste your API key in the add-on preferences

Use:
  * View3D > Sidebar (N) > FormFromLight tab
  * "Refresh list" pulls your last 25 completed models
  * Click any row to import its GLB at world origin
"""

bl_info = {
    "name": "FormFromLight",
    "author": "Resogen AI",
    "version": (1, 0, 0),
    "blender": (3, 6, 0),
    "location": "View3D > Sidebar > FormFromLight",
    "description": "Import models generated on FormFromLight directly from the cloud.",
    "category": "Import-Export",
}

import os
import bpy
import json
import tempfile
import urllib.request
import urllib.error
from bpy.props import StringProperty, IntProperty, CollectionProperty


# --------------------------------------------------------------------------- #
# Preferences

class FFLPreferences(bpy.types.AddonPreferences):
    bl_idname = __name__

    api_key: StringProperty(
        name="API key",
        description="Your ffl_… token from /profile/api-keys",
        default="",
        subtype='PASSWORD',
    )
    api_base: StringProperty(
        name="API base URL",
        description="Change only if you self-host FormFromLight",
        default="https://formfromlight.com/api/v1",
    )

    def draw(self, context):
        layout = self.layout
        layout.prop(self, "api_key")
        layout.prop(self, "api_base")
        layout.label(text="Issue a key at https://formfromlight.com/profile/api-keys")


# --------------------------------------------------------------------------- #
# Collection · per-row data

class FFLModelItem(bpy.types.PropertyGroup):
    model_id: IntProperty()
    title: StringProperty()
    glb_url: StringProperty()
    ai_model: StringProperty()
    vertices: IntProperty()


# --------------------------------------------------------------------------- #
# Operators

def _prefs(context):
    return context.preferences.addons[__name__].preferences


def _req(url, prefs, post_data=None):
    headers = {
        'Authorization': f'Bearer {prefs.api_key}',
        'Accept': 'application/json',
        'User-Agent': 'FFL-Blender/1',
    }
    if post_data is not None:
        data = json.dumps(post_data).encode('utf-8')
        headers['Content-Type'] = 'application/json'
        req = urllib.request.Request(url, data=data, headers=headers, method='POST')
    else:
        req = urllib.request.Request(url, headers=headers)
    with urllib.request.urlopen(req, timeout=30) as resp:
        return json.loads(resp.read().decode('utf-8'))


class FFL_OT_refresh(bpy.types.Operator):
    bl_idname = "ffl.refresh"
    bl_label = "Refresh list"

    def execute(self, context):
        prefs = _prefs(context)
        if not prefs.api_key:
            self.report({'ERROR'}, "Set your API key in add-on preferences first.")
            return {'CANCELLED'}

        wm = context.window_manager
        wm.ffl_items.clear()

        try:
            data = _req(f"{prefs.api_base}/models?limit=25", prefs)
        except urllib.error.HTTPError as e:
            self.report({'ERROR'}, f"HTTP {e.code}: {e.read().decode('utf-8', 'ignore')[:200]}")
            return {'CANCELLED'}
        except Exception as e:
            self.report({'ERROR'}, f"Network error: {e}")
            return {'CANCELLED'}

        models = data.get('data', [])
        completed = [m for m in models if m.get('status') == 'completed']
        for m in completed:
            try:
                detail = _req(f"{prefs.api_base}/models/{m['id']}", prefs)
            except Exception:
                continue
            urls = detail.get('urls') or {}
            glb = urls.get('glb')
            if not glb:
                continue
            it = wm.ffl_items.add()
            it.model_id = m['id']
            it.title = (m.get('title') or f"Model #{m['id']}")[:60]
            it.glb_url = glb
            it.ai_model = m.get('ai_model', '')
            it.vertices = int(detail.get('vertices') or 0)

        self.report({'INFO'}, f"Loaded {len(wm.ffl_items)} model(s).")
        return {'FINISHED'}


class FFL_OT_import(bpy.types.Operator):
    bl_idname = "ffl.import"
    bl_label = "Import"
    bl_description = "Download + import this model's GLB"
    bl_options = {'REGISTER', 'UNDO'}

    index: IntProperty()

    def execute(self, context):
        prefs = _prefs(context)
        items = context.window_manager.ffl_items
        if self.index < 0 or self.index >= len(items):
            return {'CANCELLED'}
        it = items[self.index]

        try:
            tmp = tempfile.NamedTemporaryFile(suffix='.glb', delete=False)
            with urllib.request.urlopen(it.glb_url, timeout=60) as resp:
                tmp.write(resp.read())
            tmp.close()
        except Exception as e:
            self.report({'ERROR'}, f"GLB download failed: {e}")
            return {'CANCELLED'}

        try:
            bpy.ops.import_scene.gltf(filepath=tmp.name)
            self.report({'INFO'}, f"Imported '{it.title}'")
        except Exception as e:
            self.report({'ERROR'}, f"glTF import failed: {e}")
            return {'CANCELLED'}
        finally:
            try:
                os.unlink(tmp.name)
            except OSError:
                pass

        return {'FINISHED'}


# --------------------------------------------------------------------------- #
# Panel

class FFL_PT_panel(bpy.types.Panel):
    bl_label = "FormFromLight"
    bl_idname = "FFL_PT_panel"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_category = "FormFromLight"

    def draw(self, context):
        layout = self.layout
        prefs = _prefs(context)

        if not prefs.api_key:
            layout.label(text="Set your API key in preferences.", icon='ERROR')
            layout.operator("screen.userpref_show")
            return

        layout.operator("ffl.refresh", icon='FILE_REFRESH')

        items = context.window_manager.ffl_items
        if not items:
            layout.label(text="No models loaded yet.")
            return

        box = layout.box()
        for i, it in enumerate(items):
            row = box.row(align=True)
            row.label(text=f"{it.title}   ({it.ai_model})", icon='MESH_DATA')
            op = row.operator("ffl.import", text="", icon='IMPORT')
            op.index = i


# --------------------------------------------------------------------------- #
# Registration

classes = (FFLPreferences, FFLModelItem, FFL_OT_refresh, FFL_OT_import, FFL_PT_panel)


def register():
    for c in classes:
        bpy.utils.register_class(c)
    bpy.types.WindowManager.ffl_items = CollectionProperty(type=FFLModelItem)


def unregister():
    del bpy.types.WindowManager.ffl_items
    for c in reversed(classes):
        bpy.utils.unregister_class(c)


if __name__ == "__main__":
    register()
