import os, base64, json
import dash
from dash.dependencies import Input, Output, State, ClientsideFunction, ALL
from app.views.layouts import user_login, layout_main
from app.views.layouts import layout_admin, layout_account, layout_registration
from app.models.model import add_user, check_user, get_all_users
from app.models.model import initial_text, initial_ocr_text
from app.models.model import mcq_form, generate_mcq, en_study_gpt
from app.models.model import en_correct, en_simplify, en_elevate
from app.models.model import en_describe, en_argue, en_narrate
from app.models.model import en_recontext, en_reimagine, en_reinvent
from app.models.model import en_speak, en_translate
from app.models.model import generate_image
from app.models.model import us_tutor, korean_tutor
from app.models.model import generate_prob, get_problem_content
from app.models.model import extract_text_from_images
from app.services import exam_set
from app.services.tutor import tutor_html
from app.services.complete import Complete
from app.services.exam_set import prob_i, save_to_firebase
from app.services.exam_set import list_files_from_firebase, load_from_firebase, delete_from_firebase
from app.views.view import header
from app.views.intro import manual
from concurrent.futures import ThreadPoolExecutor, as_completed
from dash import dcc, html
import dash_bootstrap_components as dbc
import time, datetime
from firebase_admin import storage
from urllib.parse import unquote
import asyncio
import aiohttp

cmp = Complete()

async def generate_exam(data):
    """Async version for batch Suneung exam creation."""
    text = data['text']
    session_data = data['session']

    suneung_output = [None] * 26
    async with aiohttp.ClientSession() as async_sess:
        tasks = []
        for i in range(25):
            c = cmp.prompt(prob_i(i))
            if c:
                tasks.append(generate_prob(i % 15, c, text, i + 1))

        completed = await asyncio.gather(*tasks)
        for (result, idx) in completed:
            suneung_output[idx] = result
    return suneung_output

def generate_exam_(data):
    """ThreadPool version for batch Suneung exam creation."""
    text = data['text']
    session_data = data['session']

    suneung_output = [None] * 26
    with ThreadPoolExecutor() as executor:
        futures = []
        for i in range(25):
            c = cmp.prompt(prob_i(i))
            if c:
                futures.append(executor.submit(generate_prob, i % 15, c, text, i + 1))

        for future in as_completed(futures):
            result, idx = future.result()
            suneung_output[idx] = result
    return suneung_output

def callbacks(app, session):
    """Main callback registrations (server-side)."""

    @app.callback(
        Output('suneung-set', 'children', allow_duplicate=True),
        Input('suneung1', 'n_clicks'),
        State('textarea', 'value'),
        State('session', 'data'),
        prevent_initial_call=True
    )
    def update_suneung_output_(n_clicks, value, session_data):
        """
        Generates a new Suneung exam set asynchronously, saves to Firestore,
        and displays in the 'suneung-set' area.
        """
        if n_clicks:
            if not session_data:
                return html.Meta(httpEquiv='refresh', content='0; url=/')
            user_email = session_data.get('user')
            start_time = time.time()
            data = {'text': value, 'session': session_data}

            loop = asyncio.new_event_loop()
            asyncio.set_event_loop(loop)
            try:
                suneung_output = loop.run_until_complete(generate_exam(data))
            finally:
                loop.close()

            exam = value + '\n\n수능 문제\n\n'
            exam += '\n\n\n'.join(problem for problem in suneung_output[1:] if problem) + '\n\n\n'

            children = html.Div([
                html.Div([
                    html.Span(
                        html.Button(
                            'Current Set',
                            id='suneung-new-set',
                            className='mx-1 mb-1 btn btn-sm btn-outline-light',
                            disabled=True,
                        )
                    ),
                    html.Span(
                        html.Button(
                            'Copy Output',
                            id='copy-suneung-set-output',
                            n_clicks=0,
                            className='mx-1 mb-1 btn btn-sm btn-outline-dark'
                        )
                    ),
                    html.Span(
                        html.Button(
                            'Edit Output',
                            id='edit-suneung-set-output',
                            n_clicks=0,
                            className='mx-1 mb-1 btn btn-sm btn-outline-dark'
                        ),
                        id='suneung-edit-btn'
                    )
                ], className='my-1', style={
                    'display': 'block', 'text-align': 'center',
                    'margin': '0 auto', 'width': '100%',
                }),
                html.Div(
                    html.Div(
                        exam,
                        id='exam',
                        className='form-control mx-0 mb-3 py-1 serif',
                        style={'color': '#101010', 'white-space': 'pre-wrap'}
                    ),
                    id='exam-container'
                )
            ])

            end_time = time.time()
            now = datetime.datetime.now()
            file_name = f'sn{now.strftime("%y%m%d_%H%M%S")}'
            save_to_firebase(file_name, exam, user_email)
            session['output_file_name'] = file_name

            return children
        raise dash.exceptions.PreventUpdate

    @app.callback(
        [Output('exam-container', 'children', allow_duplicate=True),
         Output('suneung-edit-btn', 'children', allow_duplicate=True)],
        Input('edit-suneung-set-output', 'n_clicks'),
        State('exam', 'children'),
        State('session', 'data'),
        prevent_initial_call=True
    )
    def edit_output(n_clicks, content, session_data):
        """Allows the user to edit the newly created exam set."""
        if n_clicks:
            if not session_data:
                return html.Meta(httpEquiv='refresh', content='0; url=/')
            return (
                dcc.Textarea(
                    value=content,
                    id='editarea',
                    rows=24,
                    className='form-control mx-0 mb-3 py-1 serif',
                    style={'background-color': '#EBECE8', 'color': '#252525', 'width': '100%'}
                ),
                html.Button(
                    'Save Output',
                    id='save-suneung-set-output',
                    n_clicks=0,
                    className='mx-1 mb-1 btn btn-sm btn-outline-dark'
                )
            )
        raise dash.exceptions.PreventUpdate

    @app.callback(
        [Output('exam-container', 'children', allow_duplicate=True),
         Output('suneung-edit-btn', 'children', allow_duplicate=True)],
        Input('save-suneung-set-output', 'n_clicks'),
        State('editarea', 'value'),
        State('session', 'data'),
        prevent_initial_call=True
    )
    def save_output(n_clicks, content, session_data):
        """Save the edited exam set back to Firebase."""
        if n_clicks:
            if not session_data:
                return html.Meta(httpEquiv='refresh', content='0; url=/')
            user_email = session_data.get('user')
            output_file_name = session.get('output_file_name', None)
            save_to_firebase(output_file_name, content, user_email)
            return (
                html.Div(
                    content,
                    className='form-control mx-0 mb-3 py-1 serif',
                    style={'color': '#101010', 'white-space': 'pre-wrap'}
                ),
                html.Button(
                    'Edit Output',
                    id='edit-suneung-set-output-disabled',
                    className='mx-1 mb-1 btn btn-sm btn-outline-dark',
                    disabled=True
                )
            )
        raise dash.exceptions.PreventUpdate

    @app.callback(
        Output('file-list', 'children', allow_duplicate=True),
        Input('suneung3', 'n_clicks'),
        State('session', 'data'),
        prevent_initial_call=True
    )
    def update_file_list(n_clicks, session_data):
        """Retrieves file list from Firebase (Suneung sets)."""
        if n_clicks:
            if not session_data:
                return html.Meta(httpEquiv='refresh', content='0; url=/')
            user_email = session_data.get('user')
            file_list = list_files_from_firebase(user_email)
            file_buttons = [
                html.Button(
                    file,
                    id={'type': 'file-button', 'index': file},
                    n_clicks=0,
                    className='mx-1 mb-1 btn btn-sm btn-outline-secondary',
                    style={'display': 'inline-block'}
                ) for file in file_list
            ]
            return html.Div([html.Div(file_buttons), html.Br()])
        raise dash.exceptions.PreventUpdate

    @app.callback(
        Output('file-content', 'children', allow_duplicate=True),
        Input({'type': 'file-button', 'index': ALL}, 'n_clicks'),
        State('session', 'data'),
        prevent_initial_call=True
    )
    def load_file_content(n_clicks, session_data):
        """When user clicks on one of the file buttons, load the file content."""
        ctx = dash.callback_context
        if not ctx.triggered or all(click == 0 for click in n_clicks):
            raise dash.exceptions.PreventUpdate

        triggered_prop_id = ctx.triggered[0]['prop_id']
        file_name = json.loads(triggered_prop_id.split('}.')[0] + '}')['index']

        if not session_data:
            return html.Meta(httpEquiv='refresh', content='0; url=/')
        user_email = session_data.get('user')
        session['file_name'] = file_name
        content = load_from_firebase(file_name, user_email)

        file_content_output = html.Div([
            html.Div([
                html.Span(
                    html.Button(
                        file_name,
                        className='mx-1 mb-1 btn btn-sm btn-outline-light',
                        disabled=True
                    )
                ),
                html.Span(
                    html.Button(
                        'Copy',
                        id='copy-content',
                        n_clicks=0,
                        className='mx-1 mb-1 btn btn-sm btn-outline-dark'
                    )
                ),
                html.Span(
                    html.Button(
                        'Edit',
                        id='edit-content',
                        n_clicks=0,
                        className='mx-1 mb-1 btn btn-sm btn-outline-dark'
                    ),
                    id='content-edit-btn'
                ),
                html.Span(
                    html.Button(
                        'Delete',
                        id='del-content',
                        n_clicks=0,
                        className='mx-1 mb-1 btn btn-sm btn-outline-dark'
                    ),
                    id='content-del-btn'
                )
            ], className='my-1', style={
                'display': 'block', 'text-align': 'center',
                'margin': '0 auto', 'width': '100%'
            }),
            html.Div(
                html.Div(
                    content,
                    id='content',
                    className='form-control mx-0 mb-3 py-1 serif',
                    style={'color': '#101010', 'white-space': 'pre-wrap', 'text-align': 'left'}
                ),
                id='content-container'
            )
        ])
        return file_content_output

    @app.callback(
        [Output('content-container', 'children', allow_duplicate=True),
         Output('content-edit-btn', 'children', allow_duplicate=True)],
        Input('edit-content', 'n_clicks'),
        State('content', 'children'),
        prevent_initial_call=True
    )
    def edit_content(n_clicks, content):
        """
        Allows editing the loaded file content from Firebase.
        """
        if n_clicks:
            return (
                dcc.Textarea(
                    value=content,
                    id='contentarea',
                    rows=20,
                    className='form-control mx-0 mb-3 py-1 serif',
                    style={
                        'background-color': '#EBECE8',
                        'color': '#252525',
                        'white-space': 'pre-wrap',
                        'text-align': 'left',
                        'width': '100%'
                    }
                ),
                html.Button(
                    'Save',
                    id='save-content',
                    n_clicks=0,
                    className='mx-1 mb-1 btn btn-sm btn-outline-dark'
                )
            )
        raise dash.exceptions.PreventUpdate

    @app.callback(
        [Output('content-container', 'children', allow_duplicate=True),
         Output('content-edit-btn', 'children', allow_duplicate=True)],
        Input('save-content', 'n_clicks'),
        State('contentarea', 'value'),
        State('session', 'data'),
        prevent_initial_call=True
    )
    def save_content(n_clicks, content, session_data):
        """
        Saves the edited file content back to Firebase.
        """
        if n_clicks:
            if not session_data:
                return html.Meta(httpEquiv='refresh', content='0; url=/')
            user_email = session_data.get('user')
            file_name = session.get('file_name', None)
            save_to_firebase(file_name, content, user_email)
            return (
                html.Div(
                    content,
                    className='form-control mx-0 mb-3 py-1 serif',
                    style={'color': '#101010', 'white-space': 'pre-wrap', 'text-align': 'left', 'width': '100%'}
                ),
                html.Button(
                    'Edit',
                    id='edit-content-disabled',
                    n_clicks=0,
                    className='mx-1 mb-1 btn btn-sm btn-outline-dark',
                    disabled=True
                )
            )
        raise dash.exceptions.PreventUpdate

    @app.callback(
        [Output('file-list', 'children', allow_duplicate=True),
         Output('file-content', 'children', allow_duplicate=True)],
        Input('del-content', 'n_clicks'),
        State('session', 'data'),
        prevent_initial_call=True
    )
    def delete_content(n_clicks, session_data):
        """
        Deletes the selected file from Firebase.
        """
        if n_clicks:
            if not session_data:
                return html.Meta(httpEquiv='refresh', content='0; url=/')
            user_email = session_data.get('user')
            file_name = session.get('file_name', None)
            delete_from_firebase(file_name, user_email)

            file_list = list_files_from_firebase(user_email)
            file_buttons = [
                html.Button(
                    file,
                    id={'type': 'file-button', 'index': file},
                    n_clicks=0,
                    className='mx-1 mb-1 btn btn-sm btn-outline-secondary',
                    style={'display': 'inline-block'}
                ) for file in file_list
            ]
            return (
                html.Div([html.Div(file_buttons), html.Br()]),
                html.Div()
            )
        raise dash.exceptions.PreventUpdate

    @app.callback(
        Output('header-container', 'children'),
        Input('session', 'data')
    )
    def update_header(data):
        """
        Updates the header (nav items) depending on whether user is admin or not.
        """
        if not data:
            data = {}
        is_admin = (data.get('user') == 'admin@knsedu.app')
        return header(is_admin)

    @app.callback(
        Output('registration-output', 'children'),
        Input('register-button', 'n_clicks'),
        State('reg-email', 'value'),
        State('reg-password', 'value'),
        prevent_initial_call=True
    )
    def register_user(n_clicks, email, password):
        """
        Simple registration logic.
        """
        if n_clicks and email and password:
            add_user(email, password)
            return dcc.Link('Go to Login', href='/login')

    @app.callback(
        [Output('url', 'pathname', allow_duplicate=True),
         Output('session', 'data')],
        Input('login-button', 'n_clicks'),
        State('login-email', 'value'),
        State('login-password', 'value'),
        prevent_initial_call=True
    )
    def login_user(n_clicks, email, password):
        """
        Login check => store 'user' in session
        """
        if n_clicks and email and password:
            if check_user(email, password):
                session['user'] = email
                session_data = {'user': email}
                return '/', session_data
            return dash.no_update, dash.no_update
        return dash.no_update, dash.no_update

    @app.callback(
        Output('url', 'pathname', allow_duplicate=True),
        Input('logout-button', 'n_clicks'),
        State('session', 'data'),
        prevent_initial_call=True
    )
    def logout(n_clicks, session_data):
        """
        Clears session on logout.
        """
        if n_clicks:
            session.clear()
            return '/'
        return dash.no_update

    @app.callback(
        Output('page-content', 'children', allow_duplicate=True),
        Input('url', 'pathname')
    )
    def display_page(pathname):
        """
        Main routing. 
        """
        user = session.get('user')
        text = initial_text()
        ocr_text = initial_ocr_text()

        if pathname == '/':
            return layout_main(text, ocr_text, user)
        elif pathname == '/help':
            return manual()
        elif pathname == '/register':
            return layout_registration()
        elif user:
            if pathname == '/account':
                users = get_all_users()
                return html.Div([layout_main(text, ocr_text, user), layout_account(users)])
            elif pathname == '/logout':
                session.pop('user', None)
                return layout_main(text, ocr_text, None)
            elif user == 'admin@knsedu.app' and pathname == '/admin':
                return layout_admin()
            return layout_main(text, ocr_text, user)
        return layout_main(text, ocr_text, None)

    @app.callback(
        Output('admin-output', 'children'),
        [Input('admin-add-button', 'n_clicks'),
         Input('admin-update-button', 'n_clicks'),
         Input('admin-delete-button', 'n_clicks')],
        [State('admin-email', 'value'), State('admin-password', 'value')],
        prevent_initial_call=True
    )
    def manage_user(add_clicks, update_clicks, delete_clicks, email, password):
        """
        Admin user-management stubs.
        """
        ctx = dash.callback_context
        if not ctx.triggered:
            return ''
        button_id = ctx.triggered[0]['prop_id'].split('.')[0]
        if button_id == 'admin-add-button':
            add_user(email, password)
            return f'User {email} added.'
        elif button_id == 'admin-update-button':
            return f'User {email} updated.'
        elif button_id == 'admin-delete-button':
            return f'User {email} deleted.'
        return ''

    @app.callback(
        Output('speak', 'children', allow_duplicate=True),
        Input('quickstart-speak', 'n_clicks'),
        State('textarea', 'value'),
        prevent_initial_call=True
    )
    def en_speak_us(n_clicks, value):
        """
        Speaks text with US accent TTS.
        """
        if n_clicks:
            audio_base64 = en_speak(value, 'us')
            return html.Div([
                html.Br(),
                html.Audio(
                    src=f'data:audio/mp3;base64,{audio_base64}',
                    controls=True,
                    autoPlay=True,
                    style={'display': 'block', 'width': '90%', 'margin': '0 auto'}
                ),
                html.Br()
            ])
        return dash.no_update

    @app.callback(
        Output('speak', 'children', allow_duplicate=True),
        Input('quickstart-speak-gb', 'n_clicks'),
        State('textarea', 'value'),
        prevent_initial_call=True
    )
    def en_speak_gb(n_clicks, value):
        """
        Speaks text with GB accent TTS.
        """
        if n_clicks:
            audio_base64 = en_speak(value, 'gb')
            return html.Div([
                html.Br(),
                html.Audio(
                    src=f'data:audio/mp3;base64,{audio_base64}',
                    controls=True,
                    autoPlay=True,
                    style={'display': 'block', 'width': '90%', 'margin': '0 auto'}
                ),
                html.Br()
            ])
        return dash.no_update

    @app.callback(
        Output('draw', 'children', allow_duplicate=True),
        Input('quickstart-draw', 'n_clicks'),
        State('textarea', 'value'),
        prevent_initial_call=True
    )
    def draw_image_(n_clicks, value):
        """
        Generates DALL-E image from text.
        """
        if n_clicks:
            imgs = generate_image(value)
            return html.Div([
                html.Br(),
                html.Div([
                    html.Img(
                        src=f'data:image/png;base64,{img}',
                        style={'width': '60%', 'display': 'inline-block', 'margin': '0 auto'}
                    ) for img in imgs
                ], style={'text-align': 'center'}),
                html.Br()
            ])
        return dash.no_update

    @app.callback(
        Output('translate', 'children', allow_duplicate=True),
        Input('quickstart-trans', 'n_clicks'),
        State('textarea', 'value'),
        prevent_initial_call=True
    )
    def en_translate_google(n_clicks, value):
        """
        Google-based translation => show result in 'translate'.
        """
        if n_clicks:
            translated_div = en_translate(value, 'google')
            return html.Div([
                html.Div(
                    translated_div,
                    className='form-control mx-0 serif',
                    style={'white-space': 'pre-wrap'}
                )
            ], className='mx-0 mb-3 py-1')
        return dash.no_update

    @app.callback(
        Output('translate-gpt', 'children', allow_duplicate=True),
        Input('quickstart-trans-gpt', 'n_clicks'),
        State('textarea', 'value'),
        prevent_initial_call=True
    )
    def en_translate_gpt_(n_clicks, value):
        """
        GPT-based translation => show result in 'translate-gpt'.
        """
        if n_clicks:
            t = en_translate(value, 'gpt')
            return html.Div([
                html.Div(
                    t,
                    className='form-control mx-0 serif',
                    style={'white-space': 'pre-wrap'}
                )
            ], className='mx-0 mb-3 py-1')
        return dash.no_update

    @app.callback(
        Output('quiz', 'children', allow_duplicate=True),
        Input('quickstart-quiz', 'n_clicks'),
        State('textarea', 'value'),
        prevent_initial_call=True
    )
    def en_quiz(n_clicks, value):
        """
        MCQ quiz generation.
        """
        if n_clicks:
            prompt_ = mcq_form()
            content_ = generate_mcq(value, prompt_, 4)
            ret_div = html.Div([
                html.Div(value),
                html.Br(),
                html.Div('QUIZ'),
                content_
            ])
            return html.Div([
                html.Div(
                    ret_div,
                    className='form-control mx-0',
                    style={'white-space': 'pre-wrap'}
                )
            ], className='mx-0 mb-3 py-1 serif')
        return dash.no_update

    @app.callback(
        Output('study', 'children', allow_duplicate=True),
        Input('quickstart-study', 'n_clicks'),
        State('textarea', 'value'),
        prevent_initial_call=True
    )
    def en_study_(n_clicks, value):
        """
        Creates 'study' content with GPT.
        """
        if n_clicks:
            s = html.Div([
                html.Div(value),
                html.Br(),
                html.Div('STUDY'),
                html.Br(),
                en_study_gpt(value)
            ])
            return html.Div([
                html.Div(
                    s,
                    className='form-control mx-0',
                    style={'white-space': 'pre-wrap'}
                )
            ], className='mx-0 mb-3 py-1 serif')
        return dash.no_update

    @app.callback(
        Output('american-tutoring', 'children', allow_duplicate=True),
        Input('us-tutor', 'n_clicks'),
        State('ocr-textarea', 'value'),
        prevent_initial_call=True
    )
    def american_tutoring_(n_clicks, value):
        """
        American writing tutor => corrections & feedback => display.
        """
        if n_clicks:
            content_ = us_tutor(value)
            delimiter = '4. Final Corrected Text\n\n'
            split_result = content_.split(delimiter, 1)
            modified_ = split_result[1] if len(split_result)>1 else "(No final text found)"
            t = tutor_html(value, modified_)
            return html.Div([
                html.Div(
                    html.Div([
                        html.Div(value),
                        html.Br(),
                        html.Div('((Tutoring in English))'),
                        html.Br(),
                        content_
                    ]),
                    className='form-control mx-0',
                    style={
                        'background-color': '#EBECE890',
                        'color': '#252525',
                        'white-space': 'pre-wrap'
                    }
                ),
                html.P(),
                html.Div(
                    html.Div(
                        dcc.Markdown(
                            t,
                            dangerously_allow_html=True,
                        )
                    ),
                    className='form-control mx-0',
                    style={
                        'background-color': '#EBECE8E0',
                        'color': '#252525',
                        'white-space': 'pre-wrap'
                    }
                ),
            ], className='mx-0 mb-3 py-1 serif')
        return dash.no_update

    @app.callback(
        Output('korean-tutoring', 'children', allow_duplicate=True),
        Input('korean-tutor', 'n_clicks'),
        State('ocr-textarea', 'value'),
        prevent_initial_call=True
    )
    def korean_tutoring_(n_clicks, value):
        """
        Korean writing tutor => corrections & feedback => display.
        """
        if n_clicks:
            content_ = korean_tutor(value)
            delimiter = '4. 최종 수정된 문장\n\n'
            split_result = content_.split(delimiter, 1)
            modified_ = split_result[1] if len(split_result)>1 else "(No final text found)"
            t = tutor_html(value, modified_)
            return html.Div([
                html.Div(
                    html.Div([
                        html.Div(value),
                        html.Br(),
                        html.Div('((Tutoring in Korean))'),
                        html.Br(),
                        content_
                    ]),
                    className='form-control mx-0',
                    style={
                        'background-color': '#EBECE890',
                        'color': '#252525',
                        'white-space': 'pre-wrap'
                    }
                ),
                html.P(),
                html.Div(
                    html.Div(
                        dcc.Markdown(
                            t,
                            dangerously_allow_html=True,
                        )
                    ),
                    className='form-control mx-0',
                    style={
                        'background-color': '#EBECE8E0',
                        'color': '#252525',
                        'white-space': 'pre-wrap'
                    }
                ),
            ], className='mx-0 mb-3 py-1 serif')
        return dash.no_update

    @app.callback(
        Output('textarea', 'value', allow_duplicate=True),
        Input('quickstart-correct', 'n_clicks'),
        State('textarea', 'value'),
        prevent_initial_call=True
    )
    def correct(n_clicks, value):
        """
        Correct text in place.
        """
        if n_clicks:
            return en_correct(value)
        return value

    @app.callback(
        Output('textarea', 'value', allow_duplicate=True),
        Input('quickstart-simplify', 'n_clicks'),
        State('textarea', 'value'),
        prevent_initial_call=True
    )
    def simplify(n_clicks, value):
        """
        Simplify text in place.
        """
        if n_clicks:
            return en_simplify(value)
        return value

    @app.callback(
        Output('textarea', 'value', allow_duplicate=True),
        Input('quickstart-elevate', 'n_clicks'),
        State('textarea', 'value'),
        prevent_initial_call=True
    )
    def elevate(n_clicks, value):
        """
        Elevate text style.
        """
        if n_clicks:
            return en_elevate(value)
        return value

    @app.callback(
        Output('textarea', 'value', allow_duplicate=True),
        Input('quickstart-describe', 'n_clicks'),
        State('textarea', 'value'),
        prevent_initial_call=True
    )
    def describe(n_clicks, value):
        """
        Describe text in place.
        """
        if n_clicks:
            return en_describe(value)
        return value

    @app.callback(
        Output('textarea', 'value', allow_duplicate=True),
        Input('quickstart-argue', 'n_clicks'),
        State('textarea', 'value'),
        prevent_initial_call=True
    )
    def argue(n_clicks, value):
        """
        Argue text in place.
        """
        if n_clicks:
            return en_argue(value)
        return value

    @app.callback(
        Output('textarea', 'value', allow_duplicate=True),
        Input('quickstart-narrate', 'n_clicks'),
        State('textarea', 'value'),
        prevent_initial_call=True
    )
    def narrate(n_clicks, value):
        """
        Narrate text in place.
        """
        if n_clicks:
            return en_narrate(value)
        return value

    @app.callback(
        Output('textarea', 'value', allow_duplicate=True),
        Input('quickstart-recontext', 'n_clicks'),
        State('textarea', 'value'),
        prevent_initial_call=True
    )
    def recontext(n_clicks, value):
        """
        Recontext text in place.
        """
        if n_clicks:
            return en_recontext(value)
        return value

    @app.callback(
        Output('textarea', 'value', allow_duplicate=True),
        Input('quickstart-reimagine', 'n_clicks'),
        State('textarea', 'value'),
        prevent_initial_call=True
    )
    def reimagine(n_clicks, value):
        """
        Reimagine text in place.
        """
        if n_clicks:
            return en_reimagine(value)
        return value

    @app.callback(
        Output('textarea', 'value', allow_duplicate=True),
        Input('quickstart-reinvent', 'n_clicks'),
        State('textarea', 'value'),
        prevent_initial_call=True
    )
    def reinvent(n_clicks, value):
        """
        Reinvent text in place.
        """
        if n_clicks:
            return en_reinvent(value)
        return value

    @app.callback(
        Output('ocr-textarea', 'value', allow_duplicate=True),
        [Input('upload-data', 'contents')],
        [State('upload-data', 'filename')],
        prevent_initial_call=True
    )
    def update_output(contents, filename):
        if not contents:
            raise dash.exceptions.PreventUpdate
        text = extract_text_from_images(contents, filename)
        return text

    '''# multiple=True
    @app.callback(
        Output('ocr-textarea', 'value', allow_duplicate=True),
        [Input('upload-data', 'contents')],
        [State('upload-data', 'filename')],
        prevent_initial_call=True
    )
    def update_output(contents_ls, filenames_list):
        if contents_ls is not None:
            for contents, filename in zip(contents_list, filenames_list):
                text = extract_text_from_images(contents, filename)
                return [text]
        else:
            return ['']'''

    @app.callback(
        Output('ocr-textarea','value', allow_duplicate=True),
        Input('camera-output','children'),
        prevent_initial_call=True
    )
    def run_ocr_on_camera(base64_str):
        """
        Receives base64 from camera => extract text => place in ocr-textarea
        """
        if not base64_str or not isinstance(base64_str,str) or not base64_str.startswith('data:image'):
            return dash.no_update
        dummy_filename = "camera_shot.png"
        text = extract_text_from_images(base64_str, dummy_filename)
        return text
