import React from 'react';
import superagent from 'superagent';
import { Link } from 'react-router-dom';
import {
    Container, Segment, Header, Icon, Form, Button, Message, Label,
    Grid, Divider, Modal, Tab, Loader, Dropdown, Confirm
} from 'semantic-ui-react';

import OAuthClientSecretModal from './OAuthClientSecretModal.jsx';
import OAuthGrantsList from './OAuthGrantsList.jsx';
import OAuthAuditLog from './OAuthAuditLog.jsx';
import StepUpModal from './StepUpModal.jsx';

/**
 * OAuth client editor — handles both create and edit modes.
 *
 * Authorization model (driven by the server):
 *   - `client.canManage === true`  → form is editable and action buttons render
 *   - `client.canManage === false` → read-only view; edit affordances hidden
 *   - `client.viewerIsSuperUser`    → Hard-delete button on already-deleted rows,
 *                                     plus SuperUser never gets a 428 from the
 *                                     backend so the step-up modal won't open.
 *
 * When a mutation returns 428 `step_up_required` (the Admin-owner path), we
 * cache the "pending action" and open StepUpModal. On successful verify,
 * we replay the pending action automatically.
 */
export default class OAuthClientEditor extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            loading: props.mode === 'edit',
            saving: false,
            error: null,
            successMessage: null,

            scopeCatalog: [],

            client: null,
            formName: '',
            formDescription: '',
            formLogoUrl: '',
            formHomepageUrl: '',
            formRedirectUris: '',
            formServerIps: '',
            formSelectedScopes: [],

            revealOpen: false,
            revealClientId: null,
            revealPlaintext: null,
            revealTitle: null,
            afterRevealRedirect: null,

            disableReason: '',
            disableModalOpen: false,
            deleteConfirmOpen: false,
            hardDeleteConfirmOpen: false,

            // Step-up state: when a mutation returns 428 we cache the intent
            // here and open the modal. On `onVerified` we replay pendingAction.
            stepUpOpen: false,
            pendingAction: null
        };
    }

    async componentDidMount() {
        await this.fetchScopeCatalog();
        if (this.props.mode === 'edit' && this.props.match?.params?.uniqueId) {
            await this.fetchClient(this.props.match.params.uniqueId);
        }
    }

    async fetchScopeCatalog() {
        try {
            const res = await superagent.get('/api/admin/oauth/clients/scopes').accept('json');
            this.setState({ scopeCatalog: res.body.scopes || [] });
        } catch (e) {
            console.error('Failed to fetch scope catalog', e);
        }
    }

    async fetchClient(uniqueId) {
        this.setState({ loading: true, error: null });
        try {
            const res = await superagent.get(`/api/admin/oauth/clients/${uniqueId}`).accept('json');
            const client = res.body;
            this.setState({
                loading: false,
                client,
                formName: client.name,
                formDescription: client.description || '',
                formLogoUrl: client.logoUrl || '',
                formHomepageUrl: client.homepageUrl || '',
                formRedirectUris: (client.redirectUris || []).join('\n'),
                formServerIps: (client.serverIps || []).join('\n'),
                formSelectedScopes: client.allowedScopes || []
            });
        } catch (e) {
            this.setState({ loading: false, error: e.response?.body?.message || e.message });
        }
    }

    buildPayload() {
        const splitList = (s) => s.split(/[\n,]+/).map(x => x.trim()).filter(x => x.length > 0);
        return {
            name: this.state.formName,
            description: this.state.formDescription || null,
            logoUrl: this.state.formLogoUrl || null,
            homepageUrl: this.state.formHomepageUrl || null,
            redirectUris: splitList(this.state.formRedirectUris),
            allowedScopes: this.state.formSelectedScopes,
            serverIps: splitList(this.state.formServerIps)
        };
    }

    // Wraps a mutation so that a 428 response opens the step-up modal and
    // caches the action for replay. Non-428 errors surface normally.
    async runWithStepUp(actionKey, fn) {
        try {
            await fn();
        } catch (e) {
            if (e.response?.status === 428) {
                this.setState({
                    stepUpOpen: true,
                    pendingAction: actionKey,
                    saving: false
                });
                return;
            }
            throw e;
        }
    }

    onStepUpVerified() {
        const action = this.state.pendingAction;
        this.setState({ stepUpOpen: false, pendingAction: null });
        if (!action) return;
        // Replay the original mutation. Each branch re-issues the same
        // request — the unlock is now in Redis, so the server will proceed.
        switch (action) {
            case 'save':    this.onSave(); break;
            case 'rotate':  this.onRotate(); break;
            case 'disable': this.onDisable(); break;
            case 'enable':  this.onEnable(); break;
            case 'delete':  this.onDelete(); break;
            default: break;
        }
    }

    async onCreate() {
        this.setState({ saving: true, error: null });
        try {
            const res = await superagent
                .post('/api/admin/oauth/clients')
                .accept('json')
                .send(this.buildPayload());
            const created = res.body;
            this.setState({
                saving: false,
                revealOpen: true,
                revealClientId: created.clientId,
                revealPlaintext: created.plaintextSecret,
                revealTitle: 'Client created — save the secret now',
                afterRevealRedirect: `/developers/apps/${created.uniqueId}`
            });
        } catch (e) {
            this.setState({ saving: false, error: e.response?.body?.message || e.message });
        }
    }

    async onSave() {
        if (!this.state.client) return;
        this.setState({ saving: true, error: null, successMessage: null });
        try {
            await this.runWithStepUp('save', async () => {
                await superagent
                    .put(`/api/admin/oauth/clients/${this.state.client.uniqueId}`)
                    .accept('json')
                    .send(this.buildPayload());
                await this.fetchClient(this.state.client.uniqueId);
                this.setState({ saving: false, successMessage: 'Saved.' });
            });
        } catch (e) {
            this.setState({ saving: false, error: e.response?.body?.message || e.message });
        }
    }

    async onRotate() {
        if (!this.state.client) return;
        this.setState({ saving: true, error: null });
        try {
            await this.runWithStepUp('rotate', async () => {
                const res = await superagent
                    .post(`/api/admin/oauth/clients/${this.state.client.uniqueId}/rotate_secret`)
                    .accept('json');
                const rotated = res.body;
                this.setState({
                    saving: false,
                    revealOpen: true,
                    revealClientId: rotated.clientId,
                    revealPlaintext: rotated.plaintextSecret,
                    revealTitle: 'Secret rotated — save the new secret now',
                    afterRevealRedirect: null
                });
            });
        } catch (e) {
            this.setState({ saving: false, error: e.response?.body?.message || e.message });
        }
    }

    async onDisable() {
        if (!this.state.client) return;
        this.setState({ saving: true, error: null, disableModalOpen: false });
        try {
            await this.runWithStepUp('disable', async () => {
                await superagent
                    .post(`/api/admin/oauth/clients/${this.state.client.uniqueId}/disable`)
                    .accept('json')
                    .send({ reason: this.state.disableReason || 'disabled via dashboard' });
                await this.fetchClient(this.state.client.uniqueId);
                this.setState({ saving: false, successMessage: 'Client disabled.', disableReason: '' });
            });
        } catch (e) {
            this.setState({ saving: false, error: e.response?.body?.message || e.message });
        }
    }

    async onEnable() {
        if (!this.state.client) return;
        this.setState({ saving: true, error: null });
        try {
            await this.runWithStepUp('enable', async () => {
                await superagent.post(`/api/admin/oauth/clients/${this.state.client.uniqueId}/enable`).accept('json');
                await this.fetchClient(this.state.client.uniqueId);
                this.setState({ saving: false, successMessage: 'Client enabled.' });
            });
        } catch (e) {
            this.setState({ saving: false, error: e.response?.body?.message || e.message });
        }
    }

    async onDelete() {
        if (!this.state.client) return;
        this.setState({ saving: true, error: null, deleteConfirmOpen: false });
        try {
            await this.runWithStepUp('delete', async () => {
                await superagent.delete(`/api/admin/oauth/clients/${this.state.client.uniqueId}`).accept('json');
                window.location.href = '/developers';
            });
        } catch (e) {
            this.setState({ saving: false, error: e.response?.body?.message || e.message });
        }
    }

    async onHardDelete() {
        if (!this.state.client) return;
        this.setState({ saving: true, error: null, hardDeleteConfirmOpen: false });
        try {
            await superagent
                .post(`/api/admin/oauth/clients/${this.state.client.uniqueId}/hard_delete`)
                .accept('json');
            window.location.href = '/developers';
        } catch (e) {
            this.setState({ saving: false, error: e.response?.body?.message || e.message });
        }
    }

    handleRevealClose() {
        this.setState({ revealOpen: false });
        if (this.state.afterRevealRedirect) {
            window.location.href = this.state.afterRevealRedirect;
        }
    }

    renderForm(readOnly) {
        const { scopeCatalog } = this.state;
        const scopeOptions = scopeCatalog.map(s => ({
            key: s.name,
            text: s.name,
            value: s.name,
            description: s.requiredAccessPolicies.length > 0
                ? `requires: ${s.requiredAccessPolicies.join(', ')}`
                : 'any authenticated user'
        }));

        return (
            <Form loading={this.state.saving}>
                <Form.Input
                    required
                    label="Name"
                    placeholder="e.g. fabricator-tool"
                    value={this.state.formName}
                    onChange={(e, d) => this.setState({ formName: d.value })}
                    disabled={this.props.mode === 'edit' || readOnly}
                />
                <Form.TextArea
                    label="Description"
                    placeholder="What does this client do?"
                    value={this.state.formDescription}
                    onChange={(e, d) => this.setState({ formDescription: d.value })}
                    rows={2}
                    disabled={readOnly}
                />
                <Form.Input
                    label="Logo URL"
                    placeholder="https://example.com/logo.png"
                    value={this.state.formLogoUrl}
                    onChange={(e, d) => this.setState({ formLogoUrl: d.value })}
                    disabled={readOnly}
                />
                <Form.Input
                    label="Homepage URL"
                    placeholder="https://example.com"
                    value={this.state.formHomepageUrl}
                    onChange={(e, d) => this.setState({ formHomepageUrl: d.value })}
                    disabled={readOnly}
                />

                <Divider horizontal>OAuth configuration</Divider>

                <Form.TextArea
                    required
                    label="Redirect URIs"
                    placeholder="https://example.com/cb
https://example.com/oauth/callback"
                    value={this.state.formRedirectUris}
                    onChange={(e, d) => this.setState({ formRedirectUris: d.value })}
                    rows={3}
                    disabled={readOnly}
                />
                <div style={{ fontSize: '0.85em', color: '#666', marginTop: '-0.5em', marginBottom: '1em' }}>
                    One per line. Exact-match validated at /oauth/authorize time.
                </div>

                <Form.Field required>
                    <label>Allowed scopes</label>
                    <Dropdown
                        placeholder="Select scopes this client may request"
                        fluid
                        multiple
                        search
                        selection
                        options={scopeOptions}
                        value={this.state.formSelectedScopes}
                        onChange={(e, d) => this.setState({ formSelectedScopes: d.value })}
                        disabled={readOnly}
                    />
                </Form.Field>

                <Divider horizontal>Network</Divider>

                <Form.TextArea
                    required
                    label="Server IPs (whitelisted on admin_api SG)"
                    placeholder="52.x.x.x
52.y.y.y"
                    value={this.state.formServerIps}
                    onChange={(e, d) => this.setState({ formServerIps: d.value })}
                    rows={2}
                    disabled={readOnly}
                />
                <div style={{ fontSize: '0.85em', color: '#666', marginTop: '-0.5em', marginBottom: '1em' }}>
                    One per line. These IPs must be added to the admin_api EC2 security group manually before the client can call admin_api.
                </div>
            </Form>
        );
    }

    renderReadOnlyBanner() {
        return (
            <Message icon info>
                <Icon name="lock" />
                <Message.Content>
                    <Message.Header>Read-only view</Message.Header>
                    You are not the owner of this client. Only the owner
                    (<code>{this.state.client.ownerAccountId}</code>) or a SuperUser
                    can modify it. Ownership transfer is DB-only — ask ops if the
                    owner has left.
                </Message.Content>
            </Message>
        );
    }

    renderDetailsTab() {
        const { client } = this.state;
        const readOnly = !client?.canManage;
        const disabled = client?.disabledAt;
        const deleted = client?.deletedAt;

        return (
            <Segment basic>
                {readOnly && this.renderReadOnlyBanner()}
                {deleted && (
                    <Message warning>
                        <Message.Header>This client is soft-deleted</Message.Header>
                        <p>Deleted at {new Date(client.deletedAt).toLocaleString()}. It is invisible to OAuth runtime flows. Only a SuperUser can hard-delete or restore it (restore is DB-only).</p>
                    </Message>
                )}
                {disabled && !deleted && (
                    <Message warning>
                        <Message.Header>This client is disabled</Message.Header>
                        <p>Reason: {client.disabledReason || '—'} · since {new Date(client.disabledAt).toLocaleString()}</p>
                    </Message>
                )}

                {this.state.successMessage && <Message positive>{this.state.successMessage}</Message>}
                {this.state.error && <Message negative>{this.state.error}</Message>}

                <Grid stackable columns={2}>
                    <Grid.Column width={10}>
                        {this.renderForm(readOnly)}
                        {!readOnly && (
                            <Button primary onClick={() => this.onSave()} loading={this.state.saving}>
                                <Icon name="save" /> Save changes
                            </Button>
                        )}
                    </Grid.Column>
                    <Grid.Column width={6}>
                        <Segment secondary>
                            <Header as="h4">Client ID</Header>
                            <div style={{ fontFamily: 'monospace', wordBreak: 'break-all', userSelect: 'all' }}>
                                {client.clientId}
                            </div>
                        </Segment>
                        <Segment secondary>
                            <Header as="h4">Owner</Header>
                            <div style={{ fontFamily: 'monospace', fontSize: '0.85em', wordBreak: 'break-all' }}>
                                {client.ownerAccountId}
                            </div>
                        </Segment>

                        {!readOnly && (
                            <Segment secondary>
                                <Header as="h4">Client Secret</Header>
                                <p style={{ color: '#666' }}>The plaintext is never stored. Rotate to get a new one.</p>
                                <Button onClick={() => this.onRotate()} loading={this.state.saving}>
                                    <Icon name="refresh" /> Rotate secret
                                </Button>
                            </Segment>
                        )}

                        {!readOnly && !deleted && (
                            <Segment secondary>
                                <Header as="h4">Status</Header>
                                {disabled ? (
                                    <Button positive onClick={() => this.onEnable()} loading={this.state.saving}>
                                        <Icon name="check" /> Enable
                                    </Button>
                                ) : (
                                    <Button negative onClick={() => this.setState({ disableModalOpen: true })}>
                                        <Icon name="ban" /> Disable
                                    </Button>
                                )}
                            </Segment>
                        )}

                        {!readOnly && !deleted && (
                            <Segment secondary>
                                <Header as="h4">Danger zone</Header>
                                <Button
                                    color="red"
                                    basic
                                    onClick={() => this.setState({ deleteConfirmOpen: true })}
                                >
                                    <Icon name="trash" /> Delete client
                                </Button>
                                <p style={{ fontSize: '0.85em', color: '#666', marginTop: '0.5em' }}>
                                    Soft-delete: the row stays in the DB for audit. A SuperUser can
                                    hard-delete later.
                                </p>
                            </Segment>
                        )}

                        {client.viewerIsSuperUser && deleted && (
                            <Segment secondary>
                                <Header as="h4">SuperUser</Header>
                                <Button
                                    color="red"
                                    onClick={() => this.setState({ hardDeleteConfirmOpen: true })}
                                >
                                    <Icon name="trash alternate" /> Hard-delete permanently
                                </Button>
                                <p style={{ fontSize: '0.85em', color: '#666', marginTop: '0.5em' }}>
                                    Removes the row and cascades all grants. Cannot be undone.
                                </p>
                            </Segment>
                        )}
                    </Grid.Column>
                </Grid>

                <Modal
                    open={this.state.disableModalOpen}
                    size="tiny"
                    onClose={() => this.setState({ disableModalOpen: false })}
                >
                    <Modal.Header>Disable {client?.name}?</Modal.Header>
                    <Modal.Content>
                        <Form>
                            <Form.Input
                                label="Reason"
                                placeholder="Why is this being disabled?"
                                value={this.state.disableReason}
                                onChange={(e, d) => this.setState({ disableReason: d.value })}
                            />
                        </Form>
                    </Modal.Content>
                    <Modal.Actions>
                        <Button onClick={() => this.setState({ disableModalOpen: false })}>Cancel</Button>
                        <Button negative onClick={() => this.onDisable()}>Disable</Button>
                    </Modal.Actions>
                </Modal>

                <Confirm
                    open={this.state.deleteConfirmOpen}
                    header={`Delete ${client?.name}?`}
                    content="Soft-delete: the row is marked deleted but stays in the DB. OAuth runtime flows will stop accepting it immediately."
                    onCancel={() => this.setState({ deleteConfirmOpen: false })}
                    onConfirm={() => this.onDelete()}
                    confirmButton="Delete"
                />

                <Confirm
                    open={this.state.hardDeleteConfirmOpen}
                    header={`Hard-delete ${client?.name}?`}
                    content="This removes the row entirely and cascades all grants. Cannot be undone."
                    onCancel={() => this.setState({ hardDeleteConfirmOpen: false })}
                    onConfirm={() => this.onHardDelete()}
                    confirmButton="Hard-delete permanently"
                />

                <StepUpModal
                    open={this.state.stepUpOpen}
                    clientUniqueId={client.uniqueId}
                    clientName={client.name}
                    onClose={() => this.setState({ stepUpOpen: false, pendingAction: null, saving: false })}
                    onVerified={() => this.onStepUpVerified()}
                />
            </Segment>
        );
    }

    renderCreate() {
        return (
            <Container style={{ padding: '1em' }}>
                <Segment basic>
                    <Header as="h1">
                        <Icon name="plus circle" />
                        <Header.Content>
                            Register new OAuth client
                            <Header.Subheader>Confidential client · v1</Header.Subheader>
                        </Header.Content>
                    </Header>
                </Segment>

                {this.state.error && <Message negative>{this.state.error}</Message>}

                <Segment>
                    {this.renderForm(false)}
                    <Button primary onClick={() => this.onCreate()} loading={this.state.saving}>
                        <Icon name="save" /> Create
                    </Button>
                    <Button as={Link} to="/developers" basic>Cancel</Button>
                </Segment>

                <OAuthClientSecretModal
                    open={this.state.revealOpen}
                    onClose={() => this.handleRevealClose()}
                    clientId={this.state.revealClientId}
                    plaintextSecret={this.state.revealPlaintext}
                    title={this.state.revealTitle}
                />
            </Container>
        );
    }

    renderEdit() {
        if (this.state.loading) {
            return (
                <Container style={{ padding: '2em' }}>
                    <Loader active inline="centered">Loading client…</Loader>
                </Container>
            );
        }
        if (!this.state.client) {
            return (
                <Container style={{ padding: '2em' }}>
                    <Message negative>
                        <Message.Header>Client not found</Message.Header>
                        <p>{this.state.error}</p>
                    </Message>
                    <Button as={Link} to="/developers">Back to list</Button>
                </Container>
            );
        }

        const client = this.state.client;
        const panes = [
            { menuItem: 'Details', render: () => <Tab.Pane>{this.renderDetailsTab()}</Tab.Pane> },
            { menuItem: 'Grants', render: () => <Tab.Pane><OAuthGrantsList uniqueId={client.uniqueId} /></Tab.Pane> },
            { menuItem: 'Audit log', render: () => <Tab.Pane><OAuthAuditLog uniqueId={client.uniqueId} /></Tab.Pane> }
        ];

        return (
            <Container style={{ padding: '1em' }}>
                <Segment basic clearing>
                    <Header as="h1" floated="left">
                        <Icon name="code" />
                        <Header.Content>
                            {client.name}
                            <Header.Subheader>
                                <code>{client.clientId}</code>
                                {client.disabledAt && <Label color="red" size="mini" style={{ marginLeft: 8 }}>disabled</Label>}
                                {client.deletedAt && <Label color="grey" size="mini" style={{ marginLeft: 8 }}>deleted</Label>}
                                {!client.canManage && <Label color="blue" size="mini" style={{ marginLeft: 8 }}><Icon name="lock" /> read-only</Label>}
                            </Header.Subheader>
                        </Header.Content>
                    </Header>
                    <Button as={Link} to="/developers" basic floated="right">
                        <Icon name="arrow left" /> Back
                    </Button>
                </Segment>

                <Tab panes={panes} />

                <OAuthClientSecretModal
                    open={this.state.revealOpen}
                    onClose={() => this.handleRevealClose()}
                    clientId={this.state.revealClientId}
                    plaintextSecret={this.state.revealPlaintext}
                    title={this.state.revealTitle}
                />
            </Container>
        );
    }

    render() {
        return this.props.mode === 'create' ? this.renderCreate() : this.renderEdit();
    }
}
